ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/ptytty.C
Revision: 1.44
Committed: Tue Jan 17 17:59:31 2006 UTC (18 years, 4 months ago) by ayin
Content type: text/plain
Branch: MAIN
Changes since 1.43: +84 -41 lines
Log Message:
get_pty refactoring.

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