]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - contrib/cygtermd/pty.c
Fix a controlling-terminal bug reported by Anthony Heading: Cygwin
[PuTTY.git] / contrib / cygtermd / pty.c
1 /*
2  * pty.c - pseudo-terminal handling
3  */
4
5 #define _XOPEN_SOURCE
6 #include <features.h>
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <assert.h>
12
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <termios.h>
16 #include <sys/ioctl.h>
17 #include <sys/types.h>
18 #include <pwd.h>
19
20 #include "pty.h"
21 #include "malloc.h"
22
23 static char ptyname[FILENAME_MAX];
24 int master = -1;
25
26 void pty_preinit(void)
27 {
28     /*
29      * Allocate the pty.
30      */
31     master = open("/dev/ptmx", O_RDWR);
32     if (master < 0) {
33         perror("/dev/ptmx: open");
34         exit(1);
35     }
36
37     if (grantpt(master) < 0) {
38         perror("grantpt");
39         exit(1);
40     }
41     
42     if (unlockpt(master) < 0) {
43         perror("unlockpt");
44         exit(1);
45     }
46 }
47
48 void pty_resize(int w, int h)
49 {
50     struct winsize sz;
51
52     assert(master >= 0);
53
54     sz.ws_row = h;
55     sz.ws_col = w;
56     sz.ws_xpixel = sz.ws_ypixel = 0;
57     ioctl(master, TIOCSWINSZ, &sz);
58 }
59
60 int run_program_in_pty(const struct shell_data *shdata,
61                        char *directory, char **program_args)
62 {
63     int slave, pid;
64     char *fallback_args[2];
65
66     assert(master >= 0);
67
68     ptyname[FILENAME_MAX-1] = '\0';
69     strncpy(ptyname, ptsname(master), FILENAME_MAX-1);
70
71 #if 0
72     {
73         struct winsize ws;
74         struct termios ts;
75
76         /*
77          * FIXME: think up some good defaults here
78          */
79
80         if (!ioctl(0, TIOCGWINSZ, &ws))
81             ioctl(master, TIOCSWINSZ, &ws);
82         if (!tcgetattr(0, &ts))
83             tcsetattr(master, TCSANOW, &ts);
84     }
85 #endif
86
87     slave = open(ptyname, O_RDWR | O_NOCTTY);
88     if (slave < 0) {
89         perror("slave pty: open");
90         return 1;
91     }
92
93     /*
94      * Fork and execute the command.
95      */
96     pid = fork();
97     if (pid < 0) {
98         perror("fork");
99         return 1;
100     }
101
102     if (pid == 0) {
103         int i, fd;
104
105         /*
106          * We are the child.
107          */
108         close(master);
109
110         fcntl(slave, F_SETFD, 0);    /* don't close on exec */
111         dup2(slave, 0);
112         dup2(slave, 1);
113         if (slave != 0 && slave != 1)
114             close(slave);
115         dup2(1, 2);
116         setsid();
117         setpgrp();
118         i = 0;
119 #ifdef TIOCNOTTY
120         if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
121             ioctl(fd, TIOCNOTTY, &i);
122             close(fd);
123         }
124 #endif
125         /*
126          * Make the new pty our controlling terminal. On some OSes
127          * this is done with TIOCSCTTY; Cygwin doesn't have that, so
128          * instead it's done by simply opening the pty without
129          * O_NOCTTY. This code is primarily intended for Cygwin, but
130          * it's useful to have it work in other contexts for testing
131          * purposes, so I leave the TIOCSCTTY here anyway.
132          */
133         if ((fd = open(ptyname, O_RDWR)) >= 0) {
134 #ifdef TIOCSCTTY
135             ioctl(fd, TIOCSCTTY, &i);
136 #endif
137             close(fd);
138         } else {
139             perror("slave pty: open");
140             exit(127);
141         }
142         tcsetpgrp(0, getpgrp());
143
144         for (i = 0; i < shdata->nenvvars; i++)
145             putenv(shdata->envvars[i]);
146         if (shdata->termtype)
147             putenv(shdata->termtype);
148
149         if (directory)
150             chdir(directory);
151
152         /*
153          * Use the provided shell program name, if the user gave
154          * one. Failing that, use $SHELL; failing that, look up
155          * the user's default shell in the password file; failing
156          * _that_, revert to the bog-standard /bin/sh.
157          */
158         if (!program_args) {
159             char *shell;
160             
161             shell = getenv("SHELL");
162             if (!shell) {
163                 const char *login;
164                 uid_t uid;
165                 struct passwd *pwd;
166
167                 /*
168                  * For maximum generality in the face of multiple
169                  * /etc/passwd entries with different login names and
170                  * shells but a shared uid, we start by using
171                  * getpwnam(getlogin()) if it's available - but we
172                  * insist that its uid must match our real one, or we
173                  * give up and fall back to getpwuid(getuid()).
174                  */
175                 uid = getuid();
176                 login = getlogin();
177                 if (login && (pwd = getpwnam(login)) && pwd->pw_uid == uid)
178                     shell = pwd->pw_shell;
179                 else if ((pwd = getpwuid(uid)))
180                     shell = pwd->pw_shell;
181             }
182             if (!shell)
183                 shell = "/bin/sh";
184
185             fallback_args[0] = shell;
186             fallback_args[1] = NULL;
187             program_args = fallback_args;
188         }
189
190         execv(program_args[0], program_args);
191
192         /*
193          * If we're here, exec has gone badly foom.
194          */
195         perror("exec");
196         exit(127);
197     }
198
199     close(slave);
200
201     return master;
202 }