ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/libptytty/src/ptytty.C
Revision: 1.2
Committed: Sat Jan 21 22:01:36 2006 UTC (18 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.1: +342 -267 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 /*--------------------------------*-C-*---------------------------------*
2     * File: ptytty.C
3     *----------------------------------------------------------------------*
4     *
5     * All portions of code are copyright by their respective author/s.
6     * Copyright (c) 1999-2001 Geoff Wing <gcw@pobox.com>
7 root 1.2 * Copyright (c) 2004-2006 Marc Lehmann <pcg@goof.com>
8 root 1.1 *
9     * This program is free software; you can redistribute it and/or modify
10     * it under the terms of the GNU General Public License as published by
11     * the Free Software Foundation; either version 2 of the License, or
12     * (at your option) any later version.
13     *
14     * This program is distributed in the hope that it will be useful,
15     * but WITHOUT ANY WARRANTY; without even the implied warranty of
16     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17     * GNU General Public License for more details.
18     *
19     * You should have received a copy of the GNU General Public License
20     * along with this program; if not, write to the Free Software
21     * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22     *---------------------------------------------------------------------*/
23    
24     #include "../config.h" /* NECESSARY */
25    
26 root 1.2 #include <cstdlib>
27     #include <cstring>
28    
29     #include <sys/types.h>
30     #include <sys/socket.h>
31     #include <unistd.h>
32     #include <fcntl.h>
33    
34 root 1.1 #ifdef HAVE_SYS_IOCTL_H
35     # include <sys/ioctl.h>
36     #endif
37 root 1.2 #if defined(HAVE_DEV_PTMX) && defined(HAVE_SYS_STROPTS_H)
38 root 1.1 # include <sys/stropts.h> /* for I_PUSH */
39     #endif
40     #ifdef HAVE_ISASTREAM
41     # include <stropts.h>
42     #endif
43 root 1.2 #if defined(HAVE_PTY_H)
44 root 1.1 # include <pty.h>
45 root 1.2 #elif defined(HAVE_LIBUTIL_H)
46 root 1.1 # include <libutil.h>
47 root 1.2 #elif defined(HAVE_UTIL_H)
48 root 1.1 # include <util.h>
49     #endif
50 root 1.2 #ifdef TTY_GID_SUPPORT
51     #include <grp.h>
52     #endif
53 root 1.1
54     #include <cstdio>
55 root 1.2
56     #include "rxvtutil.h"
57     #include "fdpass.h"
58     #include "ptytty.h"
59    
60     /////////////////////////////////////////////////////////////////////////////
61 root 1.1
62     /* ------------------------------------------------------------------------- *
63     * GET PSEUDO TELETYPE - MASTER AND SLAVE *
64     * ------------------------------------------------------------------------- */
65     /*
66     * Returns pty file descriptor, or -1 on failure
67     * If successful, ttydev is set to the name of the slave device.
68     * fd_tty _may_ also be set to an open fd to the slave device
69     */
70 root 1.2 #if defined(UNIX98_PTY)
71 root 1.1 static int
72     get_pty (int *fd_tty, char **ttydev)
73     {
74     int pfd;
75    
76 root 1.2 # if defined(HAVE_GETPT)
77     pfd = getpt();
78     # elif defined(HAVE_POSIX_OPENPT)
79     pfd = posix_openpt (O_RDWR);
80     # else
81     pfd = open (CLONE_DEVICE, O_RDWR | O_NOCTTY, 0);
82     # endif
83     if (pfd >= 0)
84     {
85     if (grantpt (pfd) == 0 /* change slave permissions */
86     && unlockpt (pfd) == 0)
87     { /* slave now unlocked */
88     *ttydev = strdup (ptsname (pfd)); /* get slave's name */
89     return pfd;
90     }
91 root 1.1
92 root 1.2 close (pfd);
93     }
94    
95     return -1;
96     }
97     #elif defined(HAVE_OPENPTY)
98     static int
99     get_pty (int *fd_tty, char **ttydev)
100     {
101     int pfd;
102     int res;
103     char tty_name[32];
104    
105     res = openpty (&pfd, fd_tty, tty_name, NULL, NULL);
106     if (res != -1)
107 root 1.1 {
108     *ttydev = strdup (tty_name);
109     return pfd;
110     }
111    
112 root 1.2 return -1;
113     }
114     #elif defined(HAVE__GETPTY)
115     static int
116     get_pty (int *fd_tty, char **ttydev)
117     {
118     int pfd;
119    
120 root 1.1 *ttydev = _getpty (&pfd, O_RDWR | O_NONBLOCK | O_NOCTTY, 0622, 0);
121     if (*ttydev != NULL)
122     return pfd;
123    
124 root 1.2 return -1;
125     }
126     #elif defined(HAVE_DEV_PTC)
127     static int
128     get_pty (int *fd_tty, char **ttydev)
129     {
130     int pfd;
131 root 1.1
132     if ((pfd = open ("/dev/ptc", O_RDWR | O_NOCTTY, 0)) >= 0)
133     {
134     *ttydev = strdup (ttyname (pfd));
135     return pfd;
136     }
137    
138 root 1.2 return -1;
139     }
140     #elif defined(HAVE_DEV_CLONE)
141     static int
142     get_pty (int *fd_tty, char **ttydev)
143     {
144     int pfd;
145    
146 root 1.1 if ((pfd = open ("/dev/ptym/clone", O_RDWR | O_NOCTTY, 0)) >= 0)
147     {
148     *ttydev = strdup (ptsname (pfd));
149     return pfd;
150     }
151    
152 root 1.2 return -1;
153     }
154     #else
155     /* Based on the code in openssh/openbsd-compat/bsd-openpty.c */
156     static int
157     get_pty (int *fd_tty, char **ttydev)
158     {
159     int pfd;
160     int i;
161     char pty_name[32];
162     char tty_name[32];
163     const char *majors = "pqrstuvwxyzabcde";
164     const char *minors = "0123456789abcdef";
165     for (i = 0; i < 256; i++)
166     {
167     snprintf(pty_name, 32, "/dev/pty%c%c", majors[i / 16], minors[i % 16]);
168     snprintf(tty_name, 32, "/dev/tty%c%c", majors[i / 16], minors[i % 16]);
169     if ((pfd = open (pty_name, O_RDWR | O_NOCTTY, 0)) == -1)
170     {
171     snprintf(pty_name, 32, "/dev/ptyp%d", i);
172     snprintf(tty_name, 32, "/dev/ttyp%d", i);
173     if ((pfd = open (pty_name, O_RDWR | O_NOCTTY, 0)) == -1)
174     continue;
175     }
176     if (access (tty_name, R_OK | W_OK) == 0)
177     {
178     *ttydev = strdup (tty_name);
179     return pfd;
180     }
181 root 1.1
182 root 1.2 close (pfd);
183     }
184     }
185 root 1.1 #endif
186    
187     /*----------------------------------------------------------------------*/
188     /*
189     * Returns tty file descriptor, or -1 on failure
190     */
191     static int
192     get_tty (char *ttydev)
193     {
194     return open (ttydev, O_RDWR | O_NOCTTY, 0);
195     }
196    
197     /*----------------------------------------------------------------------*/
198     /*
199     * Make our tty a controlling tty so that /dev/tty points to us
200     */
201     static int
202 root 1.2 control_tty (int fd_tty)
203 root 1.1 {
204     setsid ();
205    
206 root 1.2 #if defined(HAVE_DEV_PTMX) && defined(I_PUSH)
207 root 1.1 /*
208     * Push STREAMS modules:
209     * ptem: pseudo-terminal hardware emulation module.
210     * ldterm: standard terminal line discipline.
211     * ttcompat: V7, 4BSD and XENIX STREAMS compatibility module.
212     *
213     * After we push the STREAMS modules, the first open () on the slave side
214     * (i.e. the next section between the dashes giving us "tty opened OK")
215     * should make the "ptem" (or "ldterm" depending upon either which OS
216     * version or which set of manual pages you have) module give us a
217     * controlling terminal. We must already have close ()d the master side
218     * fd in this child process before we push STREAMS modules on because the
219     * documentation is really unclear about whether it is any close () on
220     * the master side or the last close () - i.e. a proper STREAMS dismantling
221     * close () - on the master side which causes a hang up to be sent
222     * through - Geoff Wing
223     */
224     # ifdef HAVE_ISASTREAM
225     if (isastream (fd_tty) == 1)
226     # endif
227     {
228     ioctl (fd_tty, I_PUSH, "ptem");
229     ioctl (fd_tty, I_PUSH, "ldterm");
230     ioctl (fd_tty, I_PUSH, "ttcompat");
231     }
232     #endif
233 root 1.2
234     ioctl (fd_tty, TIOCSCTTY, NULL);
235    
236     int fd = open ("/dev/tty", O_WRONLY);
237 root 1.1 if (fd < 0)
238     return -1; /* fatal */
239 root 1.2
240 root 1.1 close (fd);
241    
242     return 0;
243     }
244    
245 root 1.2 void
246     rxvt_ptytty::close_tty ()
247     {
248     if (tty < 0)
249     return;
250    
251     close (tty);
252     tty = -1;
253     }
254    
255     bool
256     rxvt_ptytty::make_controlling_tty ()
257     {
258     return control_tty (tty) >= 0;
259     }
260    
261     void
262     rxvt_ptytty::set_utf8_mode (bool on)
263     {
264     #ifdef IUTF8
265     if (pty < 0)
266     return;
267    
268     struct termios tio;
269    
270     if (tcgetattr (pty, &tio) != -1)
271     {
272     tcflag_t new_cflag = tio.c_iflag;
273    
274     if (on)
275     new_cflag |= IUTF8;
276     else
277     new_cflag &= ~IUTF8;
278    
279     if (new_cflag != tio.c_iflag)
280     {
281     tio.c_iflag = new_cflag;
282     tcsetattr (pty, TCSANOW, &tio);
283     }
284     }
285     #endif
286     }
287    
288 root 1.1 static struct ttyconf {
289     gid_t gid;
290     mode_t mode;
291    
292     ttyconf ()
293     {
294     #ifdef TTY_GID_SUPPORT
295     struct group *gr = getgrnam ("tty");
296    
297     if (gr)
298     { /* change group ownership of tty to "tty" */
299     mode = S_IRUSR | S_IWUSR | S_IWGRP;
300     gid = gr->gr_gid;
301     }
302     else
303     #endif /* TTY_GID_SUPPORT */
304     {
305     mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH;
306 root 1.2 gid = 0;
307 root 1.1 }
308     }
309     } ttyconf;
310    
311 root 1.2 rxvt_ptytty_unix::rxvt_ptytty_unix ()
312 root 1.1 {
313     name = 0;
314 root 1.2 #if UTMP_SUPPORT
315     cmd_pid = 0;
316 root 1.1 #endif
317     }
318    
319 root 1.2 rxvt_ptytty_unix::~rxvt_ptytty_unix ()
320 root 1.1 {
321 root 1.2 #if UTMP_SUPPORT
322     logout ();
323     #endif
324 root 1.1 put ();
325     }
326    
327     void
328 root 1.2 rxvt_ptytty_unix::put ()
329     {
330     chmod (name, RESTORE_TTY_MODE);
331     chown (name, 0, ttyconf.gid);
332 root 1.1
333 root 1.2 close_tty ();
334 root 1.1
335 root 1.2 if (pty >= 0)
336     close (pty);
337 root 1.1
338     free (name);
339    
340     pty = tty = -1;
341     name = 0;
342     }
343    
344     bool
345 root 1.2 rxvt_ptytty_unix::get ()
346 root 1.1 {
347     /* get master (pty) */
348     if ((pty = get_pty (&tty, &name)) < 0)
349     return false;
350    
351     fcntl (pty, F_SETFL, O_NONBLOCK);
352    
353     /* get slave (tty) */
354     if (tty < 0)
355     {
356     #ifndef NO_SETOWNER_TTYDEV
357 root 1.2 chown (name, getuid (), ttyconf.gid); /* fail silently */
358     chmod (name, ttyconf.mode);
359     # ifdef HAVE_REVOKE
360     revoke (name);
361     # endif
362 root 1.1 #endif
363    
364     if ((tty = get_tty (name)) < 0)
365     {
366     put ();
367     return false;
368     }
369     }
370    
371     return true;
372     }
373    
374 root 1.2 #if PTYTTY_HELPER
375    
376     static int sock_fd;
377     static int pid;
378    
379     struct command
380     {
381     enum { get, login, destroy } type;
382    
383     rxvt_ptytty *id;
384    
385     bool login_shell;
386     int cmd_pid;
387     char hostname[512]; // arbitrary, but should be plenty
388     };
389    
390     struct rxvt_ptytty_proxy : zero_initialized, rxvt_ptytty
391     {
392     rxvt_ptytty *id;
393    
394     ~rxvt_ptytty_proxy ();
395    
396     bool get ();
397     void login (int cmd_pid, bool login_shell, const char *hostname);
398     };
399    
400     bool
401     rxvt_ptytty_proxy::get ()
402     {
403     command cmd;
404    
405     cmd.type = command::get;
406    
407     write (sock_fd, &cmd, sizeof (cmd));
408    
409     if (read (sock_fd, &id, sizeof (id)) != sizeof (id))
410     rxvt_fatal ("protocol error while creating pty using helper process, aborting.\n");
411    
412     if (!id)
413     return false;
414    
415     if ((pty = rxvt_recv_fd (sock_fd)) < 0
416     || (tty = rxvt_recv_fd (sock_fd)) < 0)
417     rxvt_fatal ("protocol error while reading pty/tty fds from helper process, aborting.\n");
418    
419     return true;
420     }
421    
422 root 1.1 void
423 root 1.2 rxvt_ptytty_proxy::login (int cmd_pid, bool login_shell, const char *hostname)
424     {
425     command cmd;
426    
427     cmd.type = command::login;
428     cmd.id = id;
429     cmd.cmd_pid = cmd_pid;
430     cmd.login_shell = login_shell;
431     strncpy (cmd.hostname, hostname, sizeof (cmd.hostname));
432    
433     write (sock_fd, &cmd, sizeof (cmd));
434     }
435    
436     rxvt_ptytty_proxy::~rxvt_ptytty_proxy ()
437     {
438     command cmd;
439    
440     cmd.type = command::destroy;
441     cmd.id = id;
442    
443     write (sock_fd, &cmd, sizeof (cmd));
444     }
445    
446     static
447     void serve ()
448 root 1.1 {
449 root 1.2 command cmd;
450     vector<rxvt_ptytty *> ptys;
451    
452     while (read (sock_fd, &cmd, sizeof (command)) == sizeof (command))
453 root 1.1 {
454 root 1.2 if (cmd.type == command::get)
455     {
456     // -> id ptyfd ttyfd
457     cmd.id = new rxvt_ptytty_unix;
458 root 1.1
459 root 1.2 if (cmd.id->get ())
460     {
461     write (sock_fd, &cmd.id, sizeof (cmd.id));
462     ptys.push_back (cmd.id);
463 root 1.1
464 root 1.2 rxvt_send_fd (sock_fd, cmd.id->pty);
465     rxvt_send_fd (sock_fd, cmd.id->tty);
466     }
467 root 1.1 else
468 root 1.2 {
469     delete cmd.id;
470     cmd.id = 0;
471     write (sock_fd, &cmd.id, sizeof (cmd.id));
472     }
473     }
474     else if (cmd.type == command::login)
475     {
476     #if UTMP_SUPPORT
477     if (find (ptys.begin (), ptys.end (), cmd.id))
478     {
479     cmd.hostname[sizeof (cmd.hostname) - 1] = 0;
480     cmd.id->login (cmd.cmd_pid, cmd.login_shell, cmd.hostname);
481     }
482     #endif
483     }
484     else if (cmd.type == command::destroy)
485     {
486     rxvt_ptytty **pty = find (ptys.begin (), ptys.end (), cmd.id);
487 root 1.1
488 root 1.2 if (pty)
489 root 1.1 {
490 root 1.2 delete *pty;
491     ptys.erase (pty);
492 root 1.1 }
493     }
494 root 1.2 else
495     break;
496 root 1.1 }
497 root 1.2
498     // destroy all ptys
499     for (rxvt_ptytty **i = ptys.end (); i-- > ptys.begin (); )
500     delete *i;
501     }
502    
503     void rxvt_ptytty_server ()
504     {
505     int sv[2];
506    
507     if (socketpair (AF_UNIX, SOCK_STREAM, 0, sv))
508     rxvt_fatal ("could not create socket to communicate with pty/sessiondb helper, aborting.\n");
509    
510     pid = fork ();
511    
512     if (pid < 0)
513     rxvt_fatal ("could not create pty/sessiondb helper process, aborting.\n");
514    
515     if (pid)
516     {
517     // client, urxvt
518     sock_fd = sv[0];
519     close (sv[1]);
520     fcntl (sock_fd, F_SETFD, FD_CLOEXEC);
521     }
522     else
523     {
524     // server, pty-helper
525     sock_fd = sv[1];
526    
527     for (int fd = 0; fd < 1023; fd++)
528     if (fd != sock_fd)
529     close (fd);
530    
531     serve ();
532     _exit (EXIT_SUCCESS);
533     }
534     }
535    
536     #endif
537    
538     // a "factory" *g*
539     rxvt_ptytty *
540     rxvt_new_ptytty ()
541     {
542     #if PTYTTY_HELPER
543     if (pid > 0)
544     // use helper process
545     return new rxvt_ptytty_proxy;
546     else
547 root 1.1 #endif
548 root 1.2 return new rxvt_ptytty_unix;
549 root 1.1 }
550    
551     /*----------------------- end-of-file (C source) -----------------------*/
552