ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/ptytty.C
Revision: 1.45
Committed: Tue Jan 17 18:06:14 2006 UTC (18 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.44: +76 -70 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 pcg 1.1 /*--------------------------------*-C-*---------------------------------*
2 pcg 1.11 * File: ptytty.C
3 pcg 1.1 *----------------------------------------------------------------------*
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.30 * Copyright (c) 2004-2006 Marc Lehmann <pcg@goof.com>
8 pcg 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 root 1.16 #include "rxvt.h"
26 pcg 1.1
27 root 1.40 #include <cstdlib>
28     #include <cstring>
29    
30     #include <sys/types.h>
31     #include <sys/socket.h>
32     #include <unistd.h>
33     #include <fcntl.h>
34 root 1.31
35 pcg 1.1 #ifdef HAVE_SYS_IOCTL_H
36     # include <sys/ioctl.h>
37     #endif
38 root 1.12 #if defined(PTYS_ARE_PTMX) && defined(HAVE_SYS_STROPTS_H)
39 pcg 1.1 # include <sys/stropts.h> /* for I_PUSH */
40     #endif
41 pcg 1.10 #ifdef HAVE_ISASTREAM
42     # include <stropts.h>
43     #endif
44 root 1.31 #if defined(HAVE_PTY_H)
45 root 1.22 # include <pty.h>
46 root 1.31 #elif defined(HAVE_LIBUTIL_H)
47 root 1.16 # include <libutil.h>
48 root 1.31 #elif defined(HAVE_UTIL_H)
49 root 1.17 # include <util.h>
50 pcg 1.1 #endif
51    
52 root 1.16 #include <cstdio>
53     #include <grp.h>
54    
55 root 1.40 #include "rxvtutil.h"
56     #include "fdpass.h"
57 root 1.39 #include "ptytty.h"
58    
59 root 1.36 /////////////////////////////////////////////////////////////////////////////
60    
61 pcg 1.1 /* ------------------------------------------------------------------------- *
62     * GET PSEUDO TELETYPE - MASTER AND SLAVE *
63     * ------------------------------------------------------------------------- */
64     /*
65     * Returns pty file descriptor, or -1 on failure
66     * If successful, ttydev is set to the name of the slave device.
67     * fd_tty _may_ also be set to an open fd to the slave device
68     */
69 ayin 1.44 static inline int
70     get_pty_streams (int *fd_tty, char **ttydev)
71 pcg 1.1 {
72 ayin 1.44 #ifdef NO_SETOWNER_TTYDEV
73 pcg 1.4 int pfd;
74 pcg 1.1
75 root 1.45 # if defined(PTYS_ARE_GETPT)
76 ayin 1.44 pfd = getpt();
77 root 1.45 # elif defined(PTYS_ARE_POSIX)
78 ayin 1.44 pfd = posix_openpt (O_RDWR);
79     # else
80     pfd = open ("/dev/ptmx", O_RDWR | O_NOCTTY, 0);
81     # endif
82     if (pfd >= 0)
83     {
84     if (grantpt (pfd) == 0 /* change slave permissions */
85     && unlockpt (pfd) == 0)
86     { /* slave now unlocked */
87     *ttydev = strdup (ptsname (pfd)); /* get slave's name */
88     return pfd;
89     }
90 root 1.45
91 ayin 1.44 close (pfd);
92     }
93     #endif
94 root 1.45
95 ayin 1.44 return -1;
96     }
97    
98     static inline int
99     get_pty_openpty (int *fd_tty, char **ttydev)
100     {
101 pcg 1.1 #ifdef PTYS_ARE_OPENPTY
102 ayin 1.44 int pfd;
103     int res;
104 root 1.26 char tty_name[sizeof "/dev/pts/????\0"];
105 ayin 1.44
106     res = openpty (&pfd, fd_tty, tty_name, NULL, NULL);
107 root 1.27 if (res != -1)
108 pcg 1.4 {
109 pcg 1.6 *ttydev = strdup (tty_name);
110 pcg 1.4 return pfd;
111 pcg 1.1 }
112     #endif
113 root 1.45
114 ayin 1.44 return -1;
115     }
116 pcg 1.1
117 ayin 1.44 static inline int
118     get_pty__getpty (int *fd_tty, char **ttydev)
119     {
120 pcg 1.1 #ifdef PTYS_ARE__GETPTY
121 ayin 1.44 int pfd;
122    
123 pcg 1.6 *ttydev = _getpty (&pfd, O_RDWR | O_NONBLOCK | O_NOCTTY, 0622, 0);
124 pcg 1.4 if (*ttydev != NULL)
125     return pfd;
126 pcg 1.1 #endif
127 root 1.45
128 ayin 1.44 return -1;
129     }
130 pcg 1.1
131 ayin 1.44 static inline int
132     get_pty_ptc (int *fd_tty, char **ttydev)
133     {
134     #ifdef PTYS_ARE_PTC
135     int pfd;
136 pcg 1.4
137 pcg 1.6 if ((pfd = open ("/dev/ptc", O_RDWR | O_NOCTTY, 0)) >= 0)
138 pcg 1.4 {
139 root 1.16 *ttydev = strdup (ttyname (pfd));
140 pcg 1.4 return pfd;
141 pcg 1.1 }
142     #endif
143 root 1.45
144 ayin 1.44 return -1;
145     }
146 pcg 1.1
147 ayin 1.44 static inline int
148     get_pty_clone (int *fd_tty, char **ttydev)
149     {
150 pcg 1.1 #ifdef PTYS_ARE_CLONE
151 ayin 1.44 int pfd;
152    
153 pcg 1.6 if ((pfd = open ("/dev/ptym/clone", O_RDWR | O_NOCTTY, 0)) >= 0)
154 pcg 1.4 {
155 root 1.16 *ttydev = strdup (ptsname (pfd));
156 pcg 1.4 return pfd;
157 pcg 1.1 }
158     #endif
159 root 1.45
160 ayin 1.44 return -1;
161     }
162 pcg 1.1
163 ayin 1.44 static inline int
164     get_pty_numeric (int *fd_tty, char **ttydev)
165     {
166 pcg 1.1 #ifdef PTYS_ARE_NUMERIC
167 root 1.45 int pfd;
168     int idx;
169     char *c1, *c2;
170     char pty_name[] = "/dev/ptyp???";
171     char tty_name[] = "/dev/ttyp???";
172    
173     c1 = &(pty_name[sizeof (pty_name) - 4]);
174     c2 = &(tty_name[sizeof (tty_name) - 4]);
175    
176     for (idx = 0; idx < 256; idx++)
177     {
178     sprintf (c1, "%d", idx);
179     sprintf (c2, "%d", idx);
180    
181     if (access (tty_name, F_OK) < 0)
182     {
183     idx = 256;
184     break;
185     }
186    
187     if ((pfd = open (pty_name, O_RDWR | O_NOCTTY, 0)) >= 0)
188     {
189     if (access (tty_name, R_OK | W_OK) == 0)
190     {
191     *ttydev = strdup (tty_name);
192     return pfd;
193     }
194    
195     close (pfd);
196     }
197     }
198 pcg 1.1 #endif
199 root 1.45
200     return -1;
201 ayin 1.44 }
202 root 1.16
203 ayin 1.44 static inline int
204     get_pty_searched (int *fd_tty, char **ttydev)
205     {
206 pcg 1.1 #ifdef PTYS_ARE_SEARCHED
207     # ifndef PTYCHAR1
208     # define PTYCHAR1 "pqrstuvwxyz"
209     # endif
210     # ifndef PTYCHAR2
211     # define PTYCHAR2 "0123456789abcdef"
212     # endif
213 root 1.45 int pfd;
214     const char *c1, *c2;
215     char pty_name[] = "/dev/pty??";
216     char tty_name[] = "/dev/tty??";
217    
218     for (c1 = PTYCHAR1; *c1; c1++)
219     {
220     pty_name[ (sizeof (pty_name) - 3)] =
221     tty_name[ (sizeof (pty_name) - 3)] = *c1;
222    
223     for (c2 = PTYCHAR2; *c2; c2++)
224     {
225     pty_name[ (sizeof (pty_name) - 2)] =
226     tty_name[ (sizeof (pty_name) - 2)] = *c2;
227    
228     if ((pfd = open (pty_name, O_RDWR | O_NOCTTY, 0)) >= 0)
229     {
230     if (access (tty_name, R_OK | W_OK) == 0)
231     {
232     *ttydev = strdup (tty_name);
233     return pfd;
234     }
235    
236     close (pfd);
237     }
238     }
239     }
240 pcg 1.1 #endif
241 root 1.45
242     return -1;
243 ayin 1.44 }
244 root 1.16
245 ayin 1.44 static int
246     get_pty (int *fd_tty, char **ttydev)
247     {
248     int pfd;
249    
250     if ((pfd = get_pty_streams (fd_tty, ttydev)) != -1
251     || (pfd = get_pty_openpty (fd_tty, ttydev)) != -1
252     || (pfd = get_pty__getpty (fd_tty, ttydev)) != -1
253     || (pfd = get_pty_ptc (fd_tty, ttydev)) != -1
254     || (pfd = get_pty_clone (fd_tty, ttydev)) != -1
255     || (pfd = get_pty_numeric (fd_tty, ttydev)) != -1
256     || (pfd = get_pty_searched (fd_tty, ttydev)) != -1)
257     return pfd;
258 root 1.45
259 pcg 1.4 return -1;
260 pcg 1.1 }
261    
262     /*----------------------------------------------------------------------*/
263     /*
264     * Returns tty file descriptor, or -1 on failure
265     */
266 root 1.16 static int
267     get_tty (char *ttydev)
268 pcg 1.1 {
269 pcg 1.4 return open (ttydev, O_RDWR | O_NOCTTY, 0);
270 pcg 1.1 }
271    
272     /*----------------------------------------------------------------------*/
273     /*
274     * Make our tty a controlling tty so that /dev/tty points to us
275     */
276 root 1.16 static int
277 root 1.36 control_tty (int fd_tty)
278 pcg 1.1 {
279 pcg 1.4 setsid ();
280 root 1.16
281 root 1.36 # if defined(PTYS_ARE_PTMX) && defined(I_PUSH)
282 pcg 1.4 /*
283     * Push STREAMS modules:
284     * ptem: pseudo-terminal hardware emulation module.
285     * ldterm: standard terminal line discipline.
286     * ttcompat: V7, 4BSD and XENIX STREAMS compatibility module.
287     *
288 pcg 1.6 * After we push the STREAMS modules, the first open () on the slave side
289 pcg 1.4 * (i.e. the next section between the dashes giving us "tty opened OK")
290     * should make the "ptem" (or "ldterm" depending upon either which OS
291     * version or which set of manual pages you have) module give us a
292 pcg 1.6 * controlling terminal. We must already have close ()d the master side
293 pcg 1.4 * fd in this child process before we push STREAMS modules on because the
294 pcg 1.6 * documentation is really unclear about whether it is any close () on
295     * the master side or the last close () - i.e. a proper STREAMS dismantling
296     * close () - on the master side which causes a hang up to be sent
297 pcg 1.4 * through - Geoff Wing
298     */
299 root 1.36 # ifdef HAVE_ISASTREAM
300 pcg 1.4 if (isastream (fd_tty) == 1)
301 root 1.36 # endif
302 pcg 1.1 {
303 pcg 1.4 ioctl (fd_tty, I_PUSH, "ptem");
304     ioctl (fd_tty, I_PUSH, "ldterm");
305     ioctl (fd_tty, I_PUSH, "ttcompat");
306 pcg 1.1 }
307 root 1.36 # endif
308 root 1.45
309     ioctl (fd_tty, TIOCSCTTY, NULL);
310    
311     int fd = open ("/dev/tty", O_WRONLY);
312 pcg 1.4 if (fd < 0)
313     return -1; /* fatal */
314 root 1.45
315 pcg 1.4 close (fd);
316    
317     return 0;
318 pcg 1.1 }
319 root 1.16
320 root 1.36 void
321     rxvt_ptytty::close_tty ()
322     {
323     if (tty < 0)
324     return;
325    
326     close (tty);
327     tty = -1;
328     }
329    
330     bool
331     rxvt_ptytty::make_controlling_tty ()
332     {
333     return control_tty (tty) >= 0;
334     }
335    
336     void
337     rxvt_ptytty::set_utf8_mode (bool on)
338     {
339     #ifdef IUTF8
340     if (pty < 0)
341     return;
342    
343     struct termios tio;
344    
345     if (tcgetattr (pty, &tio) != -1)
346     {
347     tcflag_t new_cflag = tio.c_iflag;
348    
349     if (on)
350     new_cflag |= IUTF8;
351     else
352     new_cflag &= ~IUTF8;
353    
354     if (new_cflag != tio.c_iflag)
355     {
356     tio.c_iflag = new_cflag;
357     tcsetattr (pty, TCSANOW, &tio);
358     }
359     }
360     #endif
361     }
362    
363     /////////////////////////////////////////////////////////////////////////////
364    
365 root 1.16 #ifndef NO_SETOWNER_TTYDEV
366     static struct ttyconf {
367     gid_t gid;
368     mode_t mode;
369    
370     ttyconf ()
371     {
372     #ifdef TTY_GID_SUPPORT
373     struct group *gr = getgrnam ("tty");
374    
375     if (gr)
376     { /* change group ownership of tty to "tty" */
377     mode = S_IRUSR | S_IWUSR | S_IWGRP;
378     gid = gr->gr_gid;
379     }
380     else
381     #endif /* TTY_GID_SUPPORT */
382     {
383     mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH;
384     gid = getgid ();
385     }
386     }
387     } ttyconf;
388    
389 root 1.36 /////////////////////////////////////////////////////////////////////////////
390    
391 root 1.16 void
392 root 1.36 rxvt_ptytty_unix::privileges (rxvt_privaction action)
393 root 1.16 {
394 root 1.26 if (!name || !*name)
395 root 1.23 return;
396    
397 root 1.16 if (action == SAVE)
398     {
399     # ifndef RESET_TTY_TO_COMMON_DEFAULTS
400     /* store original tty status for restoration rxvt_clean_exit () -- rgg 04/12/95 */
401     if (lstat (name, &savestat) < 0) /* you lose out */
402 root 1.26 ;
403 root 1.16 else
404     # endif
405     {
406 root 1.26 saved = true;
407 root 1.16 chown (name, getuid (), ttyconf.gid); /* fail silently */
408     chmod (name, ttyconf.mode);
409     # ifdef HAVE_REVOKE
410     revoke (name);
411     # endif
412     }
413     }
414     else
415     { /* action == RESTORE */
416     # ifndef RESET_TTY_TO_COMMON_DEFAULTS
417 root 1.26 if (saved)
418     {
419     chmod (name, savestat.st_mode);
420     chown (name, savestat.st_uid, savestat.st_gid);
421     }
422 root 1.16 # else
423     chmod (name, (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH));
424     chown (name, 0, 0);
425     # endif
426     }
427     }
428     #endif
429    
430 root 1.36 rxvt_ptytty_unix::rxvt_ptytty_unix ()
431 root 1.16 {
432     pty = tty = -1;
433     name = 0;
434 root 1.26 #ifndef NO_SETOWNER_TTYDEV
435     saved = false;
436     #endif
437 root 1.35 #if UTMP_SUPPORT
438     cmd_pid = 0;
439     #endif
440 root 1.16 }
441    
442 root 1.36 rxvt_ptytty_unix::~rxvt_ptytty_unix ()
443 root 1.16 {
444 root 1.43 #if UTMP_SUPPORT
445 root 1.35 logout ();
446 root 1.43 #endif
447 root 1.16 put ();
448     }
449    
450     void
451 root 1.36 rxvt_ptytty_unix::put ()
452 root 1.16 {
453     #ifndef NO_SETOWNER_TTYDEV
454 root 1.28 privileges (RESTORE);
455 root 1.16 #endif
456    
457     if (pty >= 0) close (pty);
458     close_tty ();
459     free (name);
460    
461     pty = tty = -1;
462     name = 0;
463     }
464    
465     bool
466 root 1.36 rxvt_ptytty_unix::get ()
467 root 1.16 {
468     /* get master (pty) */
469     if ((pty = get_pty (&tty, &name)) < 0)
470     return false;
471    
472     fcntl (pty, F_SETFL, O_NONBLOCK);
473    
474     /* get slave (tty) */
475     if (tty < 0)
476     {
477     #ifndef NO_SETOWNER_TTYDEV
478     privileges (SAVE);
479     #endif
480    
481     if ((tty = get_tty (name)) < 0)
482     {
483     put ();
484     return false;
485     }
486     }
487    
488     return true;
489     }
490    
491 root 1.40 #if PTYTTY_HELPER
492    
493     static int sock_fd;
494     static int pid;
495    
496     struct command
497     {
498     enum { get, login, destroy } type;
499    
500     rxvt_ptytty *id;
501    
502     bool login_shell;
503     int cmd_pid;
504     char hostname[512]; // arbitrary, but should be plenty
505     };
506    
507     struct rxvt_ptytty_proxy : zero_initialized, rxvt_ptytty
508     {
509     rxvt_ptytty *id;
510    
511     ~rxvt_ptytty_proxy ();
512    
513     bool get ();
514     void login (int cmd_pid, bool login_shell, const char *hostname);
515     };
516    
517     bool
518     rxvt_ptytty_proxy::get ()
519     {
520     command cmd;
521    
522     cmd.type = command::get;
523    
524     write (sock_fd, &cmd, sizeof (cmd));
525    
526     if (read (sock_fd, &id, sizeof (id)) != sizeof (id))
527     rxvt_fatal ("protocol error while creating pty using helper process, aborting.\n");
528    
529     if (!id)
530     return false;
531    
532     if ((pty = rxvt_recv_fd (sock_fd)) < 0
533     || (tty = rxvt_recv_fd (sock_fd)) < 0)
534     rxvt_fatal ("protocol error while reading pty/tty fds from helper process, aborting.\n");
535    
536     return true;
537     }
538    
539     void
540     rxvt_ptytty_proxy::login (int cmd_pid, bool login_shell, const char *hostname)
541     {
542     command cmd;
543    
544     cmd.type = command::login;
545     cmd.id = id;
546     cmd.cmd_pid = cmd_pid;
547     cmd.login_shell = login_shell;
548     strncpy (cmd.hostname, hostname, sizeof (cmd.hostname));
549    
550     write (sock_fd, &cmd, sizeof (cmd));
551     }
552    
553     rxvt_ptytty_proxy::~rxvt_ptytty_proxy ()
554     {
555     command cmd;
556    
557     cmd.type = command::destroy;
558     cmd.id = id;
559    
560     write (sock_fd, &cmd, sizeof (cmd));
561     }
562    
563     static
564     void serve ()
565     {
566     command cmd;
567     vector<rxvt_ptytty *> ptys;
568    
569     while (read (sock_fd, &cmd, sizeof (command)) == sizeof (command))
570     {
571     if (cmd.type == command::get)
572     {
573     // -> id ptyfd ttyfd
574     cmd.id = new rxvt_ptytty_unix;
575    
576     if (cmd.id->get ())
577     {
578     write (sock_fd, &cmd.id, sizeof (cmd.id));
579     ptys.push_back (cmd.id);
580    
581     rxvt_send_fd (sock_fd, cmd.id->pty);
582     rxvt_send_fd (sock_fd, cmd.id->tty);
583     }
584     else
585     {
586     delete cmd.id;
587     cmd.id = 0;
588     write (sock_fd, &cmd.id, sizeof (cmd.id));
589     }
590     }
591     else if (cmd.type == command::login)
592     {
593 root 1.43 #if UTMP_SUPPORT
594 root 1.40 if (find (ptys.begin (), ptys.end (), cmd.id))
595     {
596     cmd.hostname[sizeof (cmd.hostname) - 1] = 0;
597     cmd.id->login (cmd.cmd_pid, cmd.login_shell, cmd.hostname);
598     }
599 root 1.43 #endif
600 root 1.40 }
601     else if (cmd.type == command::destroy)
602     {
603     rxvt_ptytty **pty = find (ptys.begin (), ptys.end (), cmd.id);
604    
605 root 1.42 if (pty)
606 root 1.40 {
607 root 1.42 delete *pty;
608 root 1.40 ptys.erase (pty);
609     }
610     }
611     else
612     break;
613     }
614    
615     // destroy all ptys
616     for (rxvt_ptytty **i = ptys.end (); i-- > ptys.begin (); )
617     delete *i;
618     }
619    
620     void rxvt_ptytty_server ()
621     {
622     int sv[2];
623    
624     if (socketpair (AF_UNIX, SOCK_STREAM, 0, sv))
625     rxvt_fatal ("could not create socket to communicate with pty/sessiondb helper, aborting.\n");
626    
627     pid = fork ();
628    
629     if (pid < 0)
630     rxvt_fatal ("could not create pty/sessiondb helper process, aborting.\n");
631    
632     if (pid)
633     {
634     // client, urxvt
635     sock_fd = sv[0];
636     close (sv[1]);
637     fcntl (sock_fd, F_SETFD, FD_CLOEXEC);
638     }
639     else
640     {
641 root 1.41 setgid (getegid ());
642     setuid (geteuid ());
643    
644 root 1.40 // server, pty-helper
645     sock_fd = sv[1];
646    
647 root 1.41 for (int fd = 0; fd < 1023; fd++)
648 root 1.42 if (fd != sock_fd && fd != 1)
649 root 1.41 close (fd);
650 root 1.40
651     serve ();
652     _exit (EXIT_SUCCESS);
653     }
654     }
655     #endif
656    
657 root 1.39 // a "factory" *g*
658 root 1.40 rxvt_ptytty *
659     rxvt_new_ptytty ()
660 root 1.39 {
661 root 1.40 #if PTYTTY_HELPER
662     if (pid > 0)
663 root 1.41 // use helper process
664     return new rxvt_ptytty_proxy;
665 root 1.40 else
666     #endif
667     return new rxvt_ptytty_unix;
668 root 1.39 }
669    
670 pcg 1.1 /*----------------------- end-of-file (C source) -----------------------*/
671 root 1.16