1 /*
2  * Shamelessly ripped out of rxvt-2.7.10 (Copyright (c) 1999-2001
3  * Geoff Wing <gcw@pobox.com>) by Hans Lub <hanslub42@gmail.com>
4  *
5  * All portions of code are copyright by their respective author/s.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *---------------------------------------------------------------------*/
21 
22 #include "rlwrap.h"
23 
24 
25 #include <stdio.h>
26 #ifdef HAVE_STDLIB_H
27 # include <stdlib.h>
28 #endif
29 #ifdef HAVE_SYS_TYPES_H
30 # include <sys/types.h>
31 #endif
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #if defined(HAVE_STRING_H)
36 # include <string.h>
37 #endif
38 #ifdef HAVE_FCNTL_H
39 # include <fcntl.h>
40 #endif
41 #ifdef HAVE_SYS_IOCTL_H
42 # include <sys/ioctl.h>
43 #endif
44 #if defined(PTYS_ARE_PTMX) && !defined(__CYGWIN32__)
45 # include <sys/stropts.h>       /* for I_PUSH */
46 #endif
47 
48 
49 
50 
51 /* ------------------------------------------------------------------------- *
52  *                  GET PSEUDO TELETYPE - MASTER AND SLAVE                   *
53  * ------------------------------------------------------------------------- */
54 /*
55  * Returns pty file descriptor, or -1 on failure
56  * If successful, ttydev is set to the name of the slave device.
57  * fd_tty _may_ also be set to an open fd to the slave device
58  */
59 
60 
61 
62 #define O_RWNN  O_RDWR | O_NDELAY | O_NOCTTY
63 
64 int
ptytty_get_pty(int * fd_tty,const char ** ttydev)65 ptytty_get_pty(int *fd_tty, const char **ttydev)
66 {
67   int pfd;
68 
69 #ifdef PTYS_ARE_OPENPTY
70   char tty_name[sizeof "/dev/pts/????\0"];
71 
72   if (openpty(&pfd, fd_tty, tty_name, NULL, NULL) != -1) {
73     *ttydev = strdup(tty_name);
74     return pfd;
75   }
76 #endif
77 
78 #ifdef PTYS_ARE__GETPTY
79   *ttydev = _getpty(&pfd, O_RWNN, 0622, 0);
80   if (*ttydev != NULL)
81     return pfd;
82 #endif
83 
84 #ifdef PTYS_ARE_GETPTY
85   char *ptydev;
86 
87   while ((ptydev = getpty()) != NULL)
88     if ((pfd = open(ptydev, O_RWNN, 0)) >= 0) {
89       *ttydev = ptydev;
90       return pfd;
91     }
92 #endif
93 
94 #if defined(HAVE_GRANTPT) && defined(HAVE_UNLOCKPT)
95 # if defined(PTYS_ARE_GETPT) || defined(PTYS_ARE_PTMX)
96   {
97     extern char *ptsname();
98 
99 #  ifdef PTYS_ARE_GETPT
100     pfd = getpt();
101 #  else
102     pfd = open("/dev/ptmx", O_RWNN, 0);
103 #  endif
104     if (pfd >= 0) {
105       if (grantpt(pfd) == 0     /* change slave permissions */
106           && unlockpt(pfd) == 0) {      /* slave now unlocked */
107         *ttydev = ptsname(pfd); /* get slave's name */
108         return pfd;
109       }
110       close(pfd);
111     }
112   }
113 # endif
114 #endif
115 
116 #ifdef PTYS_ARE_PTC
117   if ((pfd = open("/dev/ptc", O_RWNN, 0)) >= 0) {
118     *ttydev = ttyname(pfd);
119     return pfd;
120   }
121 #endif
122 
123 #ifdef PTYS_ARE_CLONE /* HP-UX */
124   if ((pfd = open("/dev/ptym/clone", O_RWNN, 0)) >= 0) {
125     *ttydev = ptsname(pfd);
126     return pfd;
127   }
128 #endif
129 
130 #ifdef PTYS_ARE_NUMERIC
131   {
132     int idx;
133     char *c1, *c2;
134     char pty_name[] = "/dev/ptyp???";
135     char tty_name[] = "/dev/ttyp???";
136 
137     c1 = &(pty_name[sizeof(pty_name) - 4]);
138     c2 = &(tty_name[sizeof(tty_name) - 4]);
139     for (idx = 0; idx < 256; idx++) {
140       snprintf1(c1, strlen(c1), "%d", idx);
141       snprintf1(c2, strlen(c2), "%d", idx);
142       if (access(tty_name, F_OK) < 0) {
143         idx = 256;
144         break;
145       }
146       if ((pfd = open(pty_name, O_RWNN, 0)) >= 0) {
147         if (access(tty_name, R_OK | W_OK) == 0) {
148           *ttydev = strdup(tty_name);
149           return pfd;
150         }
151         close(pfd);
152       }
153     }
154   }
155 #endif
156 #ifdef PTYS_ARE_SEARCHED
157   {
158     const char *c1, *c2;
159     char pty_name[] = "/dev/pty??";
160     char tty_name[] = "/dev/tty??";
161 
162 # ifndef PTYCHAR1
163 #  define PTYCHAR1      "pqrstuvwxyz"
164 # endif
165 # ifndef PTYCHAR2
166 #  define PTYCHAR2      "0123456789abcdef"
167 # endif
168     for (c1 = PTYCHAR1; *c1; c1++) {
169       pty_name[(sizeof(pty_name) - 3)] =
170         tty_name[(sizeof(pty_name) - 3)] = *c1;
171       for (c2 = PTYCHAR2; *c2; c2++) {
172         pty_name[(sizeof(pty_name) - 2)] =
173           tty_name[(sizeof(pty_name) - 2)] = *c2;
174         if ((pfd = open(pty_name, O_RWNN, 0)) >= 0) {
175           if (access(tty_name, R_OK | W_OK) == 0) {
176             *ttydev = strdup(tty_name);
177             return pfd;
178           }
179           close(pfd);
180         }
181       }
182     }
183   }
184 #endif
185   return -1;
186 }
187 
188 /*----------------------------------------------------------------------*/
189 /*
190  * Returns tty file descriptor, or -1 on failure
191  */
192 /* EXTPROTO */
193 int
ptytty_get_tty(const char * ttydev)194 ptytty_get_tty(const char *ttydev)
195 {
196   return open(ttydev, O_RDWR | O_NOCTTY, 0);
197 }
198 
199 /*----------------------------------------------------------------------*/
200 /*
201  * Make our tty a controlling tty so that /dev/tty points to us
202  */
203 /* EXTPROTO */
204 
205 
206 int
ptytty_control_tty(int fd_tty,const char * ttydev)207 ptytty_control_tty(int fd_tty, const char *ttydev)
208 {
209 
210 
211 
212   DPRINTF3(DEBUG_TERMIO, "pid: %d, tty fd: %d, dev: %s", getpid(), fd_tty,
213            ttydev);
214   DPRINTF2(DEBUG_TERMIO, "tcgetpgrp(): %d  getpgrp(): %d", tcgetpgrp(fd_tty),
215            getpgrp());
216 
217   /* ------------------- Become leader of our own session:   --------------------- */
218 # ifdef HAVE_SETSID
219   {
220     pid_t ret = setsid();
221 
222     DPRINTF2(DEBUG_TERMIO, "setsid() returned %d %s", (int)ret,
223              ERRMSG(ret < 0));
224   }
225 # endif
226 
227   /* ------------------- Get controlling tty                 --------------------- */
228 #ifndef __QNX__  /* in QNX, the only way to get a controlling tty is at process creation time, using
229                     qnx_spawn() instead of fork(). I'm too lazy to re-write my_pty_fork(), so I let rlwrap
230                     soldier on without a controlling tty */
231   {
232     int fd;
233 
234 # ifdef TIOCNOTTY
235     fd = open("/dev/tty", O_RDWR | O_NOCTTY);
236     DPRINTF1(DEBUG_TERMIO, "Voiding tty associations: previous=%s",
237              fd < 0 ? "no" : "yes");
238     if (fd >= 0) {
239       int ret = ioctl(fd, TIOCNOTTY, NULL);       /* void tty associations */
240 
241       DPRINTF2(DEBUG_TERMIO, "ioctl(..., TIOCNOTTY): %d %s", ret,
242                ERRMSG(ret < 0));
243       close(fd);
244     }
245 # endif
246     /* ---------------------------------------- */
247     fd = open("/dev/tty", O_RDWR | O_NOCTTY);
248     DPRINTF1(DEBUG_TERMIO,
249              "ptytty_control_tty(): /dev/tty has controlling tty? %s",
250              fd < 0 ? "no (good)" : "yes (bad)");
251     if (fd >= 0)
252       close(fd);                  /* ouch: still have controlling tty */
253 
254     /* ---------------------------------------- */
255 
256 #ifdef HAVE_ISASTREAM
257     if (isastream(fd_tty) == 1) {
258 #  if defined(I_SWROPT)
259         ioctl(fd_tty, I_SWROPT, 0);
260 #  endif
261 #  if defined(PTYS_ARE_PTMX) && defined(I_PUSH)
262     /*
263      * Push STREAMS modules:
264      *    ptem: pseudo-terminal hardware emulation module.
265      *    ldterm: standard terminal line discipline.
266      *    ttcompat: V7, 4BSD and XENIX STREAMS compatibility module.
267      *
268      * After we push the STREAMS modules, the first open() on the slave side
269      * (i.e. the next section between the dashes giving us "tty opened OK")
270      * should make the "ptem" (or "ldterm" depending upon either which OS
271      * version or which set of manual pages you have) module give us a
272      * controlling terminal.  We must already have close()d the master side
273      * fd in this child process before we push STREAMS modules on because the
274      * documentation is really unclear about whether it is any close() on
275      * the master side or the last close() - i.e. a proper STREAMS dismantling
276      * close() - on the master side which causes a hang up to be sent
277      * through - Geoff Wing
278      */
279 
280         DPRINTF0(DEBUG_TERMIO, "Pushing STREAMS modules");
281         ioctl(fd_tty, I_PUSH, "ptem");
282         ioctl(fd_tty, I_PUSH, "ldterm");
283         ioctl(fd_tty, I_PUSH, "ttcompat");
284 
285 #  endif
286     }
287 #endif
288     /* ---------------------------------------- */
289 # if defined(TIOCSCTTY)
290     fd = ioctl(fd_tty, TIOCSCTTY, NULL);
291     DPRINTF2(DEBUG_TERMIO, "ioctl(..,TIOCSCTTY): %d %s", fd, ERRMSG(fd < 0));
292 # elif defined(TIOCSETCTTY)
293     fd = ioctl(fd_tty, TIOCSETCTTY, NULL);
294     DPRINTF2(DEBUG_TERMIO, "ioctl(..,TIOCSETCTTY): %d %s", fd, ERRMSG(fd < 0));
295 # else
296     fd = open(ttydev, O_RDWR);
297     DPRINTF2(DEBUG_TERMIO, "tty open%s %s", (fd < 0 ? " failure" : "ed OK"),
298              ERRMSG(fd < 0));
299     if (fd >= 0)
300       close(fd);
301 # endif
302     /* ---------------------------------------- */
303     fd = open("/dev/tty", O_WRONLY);
304     DPRINTF2(DEBUG_TERMIO, "do we have controlling tty now: %s %s",
305              (fd < 0 ? "no (fatal)" : "yes (good)"), ERRMSG(fd < 0));
306     if (fd < 0)
307       myerror(WARNING|USE_ERRNO, "Could not get controlling terminal for %s", program_name);  /* mywarn called from child */
308     else
309       close(fd);
310 
311     /* ---------------------------------------- */
312     DPRINTF2(DEBUG_TERMIO, "tcgetpgrp(): %d  getpgrp(): %d", tcgetpgrp(fd_tty),
313              getpgrp());
314     /* ---------------------------------------- */
315   }
316 #endif /* ! __QNX__ */
317   return 0;
318 }
319 
320 
321 int
ptytty_openpty(int * amaster,int * aslave,const char ** name)322 ptytty_openpty(int *amaster, int *aslave, const char **name)
323 {
324   const char *scrap;
325 
326   *aslave = -1;
327   *amaster = ptytty_get_pty(aslave, &scrap);
328   if (*amaster < 0)
329     myerror(FATAL|USE_ERRNO, "Could not open master pty");
330   if (*aslave < 0)
331     *aslave = ptytty_get_tty(scrap);
332   if (*aslave < 0)
333     myerror(FATAL|USE_ERRNO, "Could not open slave pty %s", scrap);
334   else
335     if (name != NULL)
336       *name = scrap;
337 
338   return 0;
339 }
340