]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/pty.c
9774deafc945f78db85306ca39eeb0ebf15bd98a
[PuTTY.git] / unix / pty.c
1 #define _XOPEN_SOURCE
2 #define _XOPEN_SOURCE_EXTENDED
3 #include <features.h>
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <signal.h>
10 #include <fcntl.h>
11 #include <termios.h>
12 #include <grp.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <sys/ioctl.h>
16
17 #include "putty.h"
18
19 #ifndef FALSE
20 #define FALSE 0
21 #endif
22 #ifndef TRUE
23 #define TRUE 1
24 #endif
25
26 int pty_master_fd;
27 static int pty_child_pid;
28 static sig_atomic_t pty_child_dead;
29 char **pty_argv;
30
31 int pty_child_is_dead(void)
32 {
33     return pty_child_dead;
34 }
35
36 static void pty_size(void);
37
38 static void sigchld_handler(int signum)
39 {
40     pid_t pid;
41     int status;
42     pid = waitpid(-1, &status, WNOHANG);
43     if (pid == pty_child_pid && (WIFEXITED(status) || WIFSIGNALED(status)))
44         pty_child_dead = TRUE;  
45 }
46
47 /*
48  * Called to set up the pty.
49  * 
50  * Returns an error message, or NULL on success.
51  *
52  * Also places the canonical host name into `realhost'. It must be
53  * freed by the caller.
54  */
55 static char *pty_init(char *host, int port, char **realhost, int nodelay)
56 {
57     int slavefd;
58     char name[FILENAME_MAX];
59     pid_t pid, pgrp;
60
61 #ifdef BSD_PTYS
62     {
63         const char chars1[] = "pqrstuvwxyz";
64         const char chars2[] = "0123456789abcdef";
65         const char *p1, *p2;
66         char master_name[20];
67
68         for (p1 = chars1; *p1; p1++)
69             for (p2 = chars2; *p2; p2++) {
70                 sprintf(master_name, "/dev/pty%c%c", *p1, *p2);
71                 pty_master_fd = open(master_name, O_RDWR);
72                 if (pty_master_fd >= 0) {
73                     if (geteuid() == 0 ||
74                         access(master_name, R_OK | W_OK) == 0)
75                         goto got_one;
76                     close(pty_master_fd);
77                 }
78             }
79
80         /* If we get here, we couldn't get a tty at all. */
81         fprintf(stderr, "pterm: unable to open a pseudo-terminal device\n");
82         exit(1);
83
84         got_one:
85         strcpy(name, master_name);
86         name[5] = 't';                 /* /dev/ptyXX -> /dev/ttyXX */
87     }
88 #else
89     pty_master_fd = open("/dev/ptmx", O_RDWR);
90
91     if (pty_master_fd < 0) {
92         perror("/dev/ptmx: open");
93         exit(1);
94     }
95
96     if (grantpt(pty_master_fd) < 0) {
97         perror("grantpt");
98         exit(1);
99     }
100     
101     if (unlockpt(pty_master_fd) < 0) {
102         perror("unlockpt");
103         exit(1);
104     }
105
106     name[FILENAME_MAX-1] = '\0';
107     strncpy(name, ptsname(pty_master_fd), FILENAME_MAX-1);
108 #endif
109
110     /*
111      * Fork and execute the command.
112      */
113     pid = fork();
114     if (pid < 0) {
115         perror("fork");
116         exit(1);
117     }
118
119     if (pid == 0) {
120         int i;
121         /*
122          * We are the child.
123          */
124
125         slavefd = open(name, O_RDWR);
126         if (slavefd < 0) {
127             perror("slave pty: open");
128             exit(1);
129         }
130
131 #ifdef BSD_PTYS
132         /* We need to chown/chmod the /dev/ttyXX device. */
133         {
134             struct group *gp = getgrnam("tty");
135             fchown(slavefd, getuid(), gp ? gp->gr_gid : -1);
136             fchmod(slavefd, 0600);
137         }
138 #endif
139
140         close(pty_master_fd);
141         close(0);
142         close(1);
143         close(2);
144         fcntl(slavefd, F_SETFD, 0);    /* don't close on exec */
145         dup2(slavefd, 0);
146         dup2(slavefd, 1);
147         dup2(slavefd, 2);
148         setsid();
149         ioctl(slavefd, TIOCSCTTY, 1);
150         pgrp = getpid();
151         tcsetpgrp(slavefd, pgrp);
152         setpgrp();
153         close(open(name, O_WRONLY, 0));
154         setpgrp();
155         /* In case we were setgid-utmp or setuid-root, drop privs. */
156         setgid(getgid());
157         setuid(getuid());
158         /* Close everything _else_, for tidiness. */
159         for (i = 3; i < 1024; i++)
160             close(i);
161         {
162             char term_env_var[10 + sizeof(cfg.termtype)];
163             sprintf(term_env_var, "TERM=%s", cfg.termtype);
164             putenv(term_env_var);
165         }
166         if (pty_argv)
167             execvp(pty_argv[0], pty_argv);
168         else
169             execl(getenv("SHELL"), getenv("SHELL"), NULL);
170         /*
171          * If we're here, exec has gone badly foom.
172          */
173         perror("exec");
174         exit(127);
175     } else {
176         close(slavefd);
177         pty_child_pid = pid;
178         pty_child_dead = FALSE;
179         signal(SIGCHLD, sigchld_handler);
180     }
181
182     return NULL;
183 }
184
185 /*
186  * Called to send data down the pty.
187  */
188 static int pty_send(char *buf, int len)
189 {
190     while (len > 0) {
191         int ret = write(pty_master_fd, buf, len);
192         if (ret < 0) {
193             perror("write pty master");
194             exit(1);
195         }
196         buf += ret;
197         len -= ret;
198     }
199     return 0;
200 }
201
202 /*
203  * Called to query the current socket sendability status.
204  */
205 static int pty_sendbuffer(void)
206 {
207     return 0;
208 }
209
210 /*
211  * Called to set the size of the window
212  */
213 static void pty_size(void)
214 {
215     struct winsize size;
216
217     size.ws_row = (unsigned short)rows;
218     size.ws_col = (unsigned short)cols;
219     ioctl(pty_master_fd, TIOCSWINSZ, (void *)&size);
220     return;
221 }
222
223 /*
224  * Send special codes.
225  */
226 static void pty_special(Telnet_Special code)
227 {
228     /* Do nothing! */
229     return;
230 }
231
232 static Socket pty_socket(void)
233 {
234     return NULL;                       /* shouldn't ever be needed */
235 }
236
237 static int pty_sendok(void)
238 {
239     return 1;
240 }
241
242 static void pty_unthrottle(int backlog)
243 {
244     /* do nothing */
245 }
246
247 static int pty_ldisc(int option)
248 {
249     return 0;                          /* neither editing nor echoing */
250 }
251
252 static int pty_exitcode(void)
253 {
254     /* Shouldn't ever be required */
255     return 0;
256 }
257
258 Backend pty_backend = {
259     pty_init,
260     pty_send,
261     pty_sendbuffer,
262     pty_size,
263     pty_special,
264     pty_socket,
265     pty_exitcode,
266     pty_sendok,
267     pty_ldisc,
268     pty_unthrottle,
269     1
270 };