Fix typos.
[dana/urxvt.git] / src / ptytty.C
1 // This file is part of libptytty. Do not make local modifications.
2 // http://software.schmorp.de/pkg/libptytty
3
4 /*----------------------------------------------------------------------*
5  * File:        ptytty.C
6  *----------------------------------------------------------------------*
7  *
8  * All portions of code are copyright by their respective author/s.
9  * Copyright (c) 1999-2001 Geoff Wing <gcw@pobox.com>
10  * Copyright (c) 2004-2006 Marc Lehmann <pcg@goof.com>
11  * Copyright (c) 2006      Emanuele Giaquinta <e.giaquinta@glauco.it>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26  *---------------------------------------------------------------------*/
27
28 #include "../config.h"
29
30 #include "ptytty.h"
31
32 #include <cstdlib>
33 #include <cstring>
34 #include <csignal>
35
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40
41 #ifdef HAVE_SYS_IOCTL_H
42 # include <sys/ioctl.h>
43 #endif
44 #if defined(HAVE_DEV_PTMX) && defined(HAVE_SYS_STROPTS_H)
45 # include <sys/stropts.h>      /* for I_PUSH */
46 #endif
47 #if defined(HAVE_ISASTREAM) && defined(HAVE_STROPTS_H)
48 # include <stropts.h>
49 #endif
50 #if defined(HAVE_PTY_H)
51 # include <pty.h>
52 #elif defined(HAVE_LIBUTIL_H)
53 # include <libutil.h>
54 #elif defined(HAVE_UTIL_H)
55 # include <util.h>
56 #endif
57 #ifdef TTY_GID_SUPPORT
58 #include <grp.h>
59 #endif
60
61 #include <cstdio>
62
63 /////////////////////////////////////////////////////////////////////////////
64
65 /* ------------------------------------------------------------------------- *
66  *                  GET PSEUDO TELETYPE - MASTER AND SLAVE                   *
67  * ------------------------------------------------------------------------- */
68 /*
69  * Returns pty file descriptor, or -1 on failure
70  * If successful, ttydev is set to the name of the slave device.
71  * fd_tty _may_ also be set to an open fd to the slave device
72  */
73 #if defined(UNIX98_PTY)
74
75   static int
76   get_pty (int *fd_tty, char **ttydev)
77   {
78     int pfd;
79
80 # if defined(HAVE_GETPT)
81     pfd = getpt();
82 # elif defined(HAVE_POSIX_OPENPT)
83     pfd = posix_openpt (O_RDWR);
84 # else
85     pfd = open (CLONE_DEVICE, O_RDWR | O_NOCTTY, 0);
86 # endif
87
88     if (pfd >= 0)
89       {
90         if (grantpt (pfd) == 0  /* change slave permissions */
91             && unlockpt (pfd) == 0)
92           {
93             /* slave now unlocked */
94             *ttydev = strdup (ptsname (pfd));   /* get slave's name */
95             return pfd;
96           }
97
98         close (pfd);
99       }
100
101     return -1;
102   }
103
104 #elif defined(HAVE_OPENPTY)
105
106   static int
107   get_pty (int *fd_tty, char **ttydev)
108   {
109     int pfd;
110     int res;
111
112     res = openpty (&pfd, fd_tty, NULL, NULL, NULL);
113
114     if (res != -1)
115       {
116         *ttydev = strdup (ttyname (*fd_tty));
117         return pfd;
118       }
119
120     return -1;
121   }
122
123 #elif defined(HAVE__GETPTY)
124
125   static int
126   get_pty (int *fd_tty, char **ttydev)
127   {
128     int pfd;
129     char *slave;
130
131     slave = _getpty (&pfd, O_RDWR | O_NONBLOCK | O_NOCTTY, 0622, 0);
132
133     if (slave != NULL)
134       {
135         *ttydev = strdup (slave);
136         return pfd;
137       }
138
139     return -1;
140   }
141
142 #elif defined(HAVE_DEV_PTC)
143
144   static int
145   get_pty (int *fd_tty, char **ttydev)
146   {
147     int pfd;
148
149     if ((pfd = open ("/dev/ptc", O_RDWR | O_NOCTTY, 0)) >= 0)
150       {
151         *ttydev = strdup (ttyname (pfd));
152         return pfd;
153       }
154
155     return -1;
156   }
157
158 #elif defined(HAVE_DEV_CLONE)
159
160   static int
161   get_pty (int *fd_tty, char **ttydev)
162   {
163     int pfd;
164
165     if ((pfd = open ("/dev/ptym/clone", O_RDWR | O_NOCTTY, 0)) >= 0)
166       {
167         *ttydev = strdup (ptsname (pfd));
168         return pfd;
169       }
170
171     return -1;
172   }
173
174 #else
175
176   /* Based on the code in openssh/openbsd-compat/bsd-openpty.c */
177   static int
178   get_pty (int *fd_tty, char **ttydev)
179   {
180     int pfd;
181     int i;
182     char pty_name[32];
183     char tty_name[32];
184     const char *majors = "pqrstuvwxyzabcde";
185     const char *minors = "0123456789abcdef";
186
187     for (i = 0; i < 256; i++)
188       {
189         snprintf(pty_name, 32, "/dev/pty%c%c", majors[i / 16], minors[i % 16]);
190         snprintf(tty_name, 32, "/dev/tty%c%c", majors[i / 16], minors[i % 16]);
191
192         if ((pfd = open (pty_name, O_RDWR | O_NOCTTY, 0)) == -1)
193           {
194             snprintf(pty_name, 32, "/dev/ptyp%d", i);
195             snprintf(tty_name, 32, "/dev/ttyp%d", i);
196             if ((pfd = open (pty_name, O_RDWR | O_NOCTTY, 0)) == -1)
197               continue;
198           }
199
200         if (access (tty_name, R_OK | W_OK) == 0)
201           {
202             *ttydev = strdup (tty_name);
203             return pfd;
204           }
205
206         close (pfd);
207       }
208
209     return -1;
210   }
211
212 #endif
213
214 /*----------------------------------------------------------------------*/
215 /*
216  * Returns tty file descriptor, or -1 on failure
217  */
218 static int
219 get_tty (char *ttydev)
220 {
221   return open (ttydev, O_RDWR | O_NOCTTY, 0);
222 }
223
224 /*----------------------------------------------------------------------*/
225 /*
226  * Make our tty a controlling tty so that /dev/tty points to us
227  */
228 static int
229 control_tty (int fd_tty)
230 {
231   int fd;
232
233   setsid ();
234
235 #if defined(HAVE_DEV_PTMX) && defined(I_PUSH)
236   /*
237    * Push STREAMS modules:
238    *    ptem: pseudo-terminal hardware emulation module.
239    *    ldterm: standard terminal line discipline.
240    *    ttcompat: V7, 4BSD and XENIX STREAMS compatibility module.
241    *
242    * After we push the STREAMS modules, the first open () on the slave side
243    * (i.e. the next section between the dashes giving us "tty opened OK")
244    * should make the "ptem" (or "ldterm" depending upon either which OS
245    * version or which set of manual pages you have) module give us a
246    * controlling terminal.  We must already have close ()d the master side
247    * fd in this child process before we push STREAMS modules on because the
248    * documentation is really unclear about whether it is any close () on
249    * the master side or the last close () - i.e. a proper STREAMS dismantling
250    * close () - on the master side which causes a hang up to be sent
251    * through - Geoff Wing
252    */
253 #if defined(HAVE_ISASTREAM) && defined(HAVE_STROPTS_H)
254   if (isastream (fd_tty) == 1)
255 # endif
256     {
257       ioctl (fd_tty, I_PUSH, "ptem");
258       ioctl (fd_tty, I_PUSH, "ldterm");
259       ioctl (fd_tty, I_PUSH, "ttcompat");
260     }
261 #endif
262
263 #ifdef TIOCSCTTY
264   ioctl (fd_tty, TIOCSCTTY, NULL);
265 #else
266   fd = open (ttyname (fd_tty), O_RDWR);
267   if (fd >= 0)
268     close (fd);
269 #endif
270
271   fd = open ("/dev/tty", O_WRONLY);
272   if (fd < 0)
273     return -1; /* fatal */
274
275   close (fd);
276
277   return 0;
278 }
279
280 void
281 ptytty::close_tty ()
282 {
283   if (tty < 0)
284     return;
285
286   close (tty);
287   tty = -1;
288 }
289
290 bool
291 ptytty::make_controlling_tty ()
292 {
293   return control_tty (tty) >= 0;
294 }
295
296 void
297 ptytty::set_utf8_mode (bool on)
298 {
299 #ifdef IUTF8
300   if (pty < 0)
301     return;
302
303   struct termios tio;
304
305   if (tcgetattr (pty, &tio) != -1)
306     {
307       tcflag_t new_cflag = tio.c_iflag;
308
309       if (on)
310         new_cflag |= IUTF8;
311       else
312         new_cflag &= ~IUTF8;
313
314       if (new_cflag != tio.c_iflag)
315         {
316           tio.c_iflag = new_cflag;
317           tcsetattr (pty, TCSANOW, &tio);
318         }
319     }
320 #endif
321 }
322
323 static struct ttyconf {
324   gid_t gid;
325   mode_t mode;
326
327   ttyconf ()
328     {
329 #ifdef TTY_GID_SUPPORT
330       struct group *gr = getgrnam ("tty");
331
332       if (gr)
333         {
334           /* change group ownership of tty to "tty" */
335           mode = S_IRUSR | S_IWUSR | S_IWGRP;
336           gid = gr->gr_gid;
337         }
338       else
339 #endif /* TTY_GID_SUPPORT */
340         {
341           mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH;
342           gid = 0;
343         }
344     }
345 } ttyconf;
346
347 ptytty_unix::ptytty_unix ()
348 {
349   name = 0;
350 #if UTMP_SUPPORT
351   cmd_pid = 0;
352 #endif
353 }
354
355 ptytty_unix::~ptytty_unix ()
356 {
357 #if UTMP_SUPPORT
358   logout ();
359 #endif
360   put ();
361 }
362
363 void
364 ptytty_unix::put ()
365 {
366   if (name)
367     {
368       chmod (name, RESTORE_TTY_MODE);
369       chown (name, 0, ttyconf.gid);
370     }
371
372   close_tty ();
373
374   if (pty >= 0)
375     close (pty);
376
377   free (name);
378
379   pty = tty = -1;
380   name = 0;
381 }
382
383 bool
384 ptytty_unix::get ()
385 {
386   /* get master (pty) */
387   if ((pty = get_pty (&tty, &name)) < 0)
388     return false;
389
390   fcntl (pty, F_SETFL, O_NONBLOCK);
391
392   /* get slave (tty) */
393   if (tty < 0)
394     {
395 #ifndef NO_SETOWNER_TTYDEV
396       chown (name, getuid (), ttyconf.gid);      /* fail silently */
397       chmod (name, ttyconf.mode);
398 # ifdef HAVE_REVOKE
399       revoke (name);
400 # endif
401 #endif
402
403       if ((tty = get_tty (name)) < 0)
404         {
405           put ();
406           return false;
407         }
408     }
409
410   return true;
411 }
412