054d8535 |
1 | #define _XOPEN_SOURCE |
88e6b9ca |
2 | #define _XOPEN_SOURCE_EXTENDED |
054d8535 |
3 | #include <features.h> |
4 | |
1709795f |
5 | #include <stdio.h> |
6 | #include <stdlib.h> |
054d8535 |
7 | #include <string.h> |
8 | #include <unistd.h> |
a0e16eb1 |
9 | #include <signal.h> |
054d8535 |
10 | #include <fcntl.h> |
88e6b9ca |
11 | #include <termios.h> |
a0e16eb1 |
12 | #include <sys/types.h> |
13 | #include <sys/wait.h> |
88e6b9ca |
14 | #include <sys/ioctl.h> |
1709795f |
15 | |
16 | #include "putty.h" |
17 | |
18 | #ifndef FALSE |
19 | #define FALSE 0 |
20 | #endif |
21 | #ifndef TRUE |
22 | #define TRUE 1 |
23 | #endif |
24 | |
054d8535 |
25 | int pty_master_fd; |
a0e16eb1 |
26 | static int pty_child_pid; |
27 | static sig_atomic_t pty_child_dead; |
6169c758 |
28 | char **pty_argv; |
054d8535 |
29 | |
a0e16eb1 |
30 | int pty_child_is_dead(void) |
31 | { |
32 | return pty_child_dead; |
33 | } |
34 | |
1709795f |
35 | static void pty_size(void); |
36 | |
a0e16eb1 |
37 | static void sigchld_handler(int signum) |
38 | { |
39 | pid_t pid; |
40 | int status; |
41 | pid = waitpid(-1, &status, WNOHANG); |
42 | if (pid == pty_child_pid && (WIFEXITED(status) || WIFSIGNALED(status))) |
43 | pty_child_dead = TRUE; |
44 | } |
45 | |
1709795f |
46 | /* |
47 | * Called to set up the pty. |
48 | * |
49 | * Returns an error message, or NULL on success. |
50 | * |
51 | * Also places the canonical host name into `realhost'. It must be |
52 | * freed by the caller. |
53 | */ |
54 | static char *pty_init(char *host, int port, char **realhost, int nodelay) |
55 | { |
054d8535 |
56 | int slavefd; |
57 | char name[FILENAME_MAX]; |
d37c2d81 |
58 | pid_t pid, pgrp; |
054d8535 |
59 | |
60 | pty_master_fd = open("/dev/ptmx", O_RDWR); |
61 | |
62 | if (pty_master_fd < 0) { |
63 | perror("/dev/ptmx: open"); |
64 | exit(1); |
65 | } |
66 | |
67 | if (grantpt(pty_master_fd) < 0) { |
68 | perror("grantpt"); |
69 | exit(1); |
70 | } |
71 | |
72 | if (unlockpt(pty_master_fd) < 0) { |
73 | perror("unlockpt"); |
74 | exit(1); |
75 | } |
76 | |
77 | name[FILENAME_MAX-1] = '\0'; |
78 | strncpy(name, ptsname(pty_master_fd), FILENAME_MAX-1); |
79 | |
054d8535 |
80 | /* |
81 | * Fork and execute the command. |
82 | */ |
83 | pid = fork(); |
84 | if (pid < 0) { |
85 | perror("fork"); |
88e6b9ca |
86 | exit(1); |
054d8535 |
87 | } |
88 | |
89 | if (pid == 0) { |
90 | int i; |
91 | /* |
92 | * We are the child. |
93 | */ |
d37c2d81 |
94 | |
95 | slavefd = open(name, O_RDWR); |
96 | if (slavefd < 0) { |
97 | perror("slave pty: open"); |
98 | exit(1); |
99 | } |
100 | |
054d8535 |
101 | close(pty_master_fd); |
102 | close(0); |
103 | close(1); |
104 | close(2); |
105 | fcntl(slavefd, F_SETFD, 0); /* don't close on exec */ |
106 | dup2(slavefd, 0); |
107 | dup2(slavefd, 1); |
108 | dup2(slavefd, 2); |
109 | setsid(); |
d37c2d81 |
110 | ioctl(slavefd, TIOCSCTTY, 1); |
111 | pgrp = getpid(); |
112 | tcsetpgrp(slavefd, pgrp); |
113 | setpgrp(); |
114 | close(open(name, O_WRONLY, 0)); |
054d8535 |
115 | setpgrp(); |
054d8535 |
116 | /* Close everything _else_, for tidiness. */ |
117 | for (i = 3; i < 1024; i++) |
118 | close(i); |
fe9548aa |
119 | { |
120 | char term_env_var[10 + sizeof(cfg.termtype)]; |
121 | sprintf(term_env_var, "TERM=%s", cfg.termtype); |
122 | putenv(term_env_var); |
123 | } |
6169c758 |
124 | if (pty_argv) |
125 | execvp(pty_argv[0], pty_argv); |
126 | else |
127 | execl(getenv("SHELL"), getenv("SHELL"), NULL); |
054d8535 |
128 | /* |
129 | * If we're here, exec has gone badly foom. |
130 | */ |
131 | perror("exec"); |
132 | exit(127); |
133 | } else { |
134 | close(slavefd); |
a0e16eb1 |
135 | pty_child_pid = pid; |
136 | pty_child_dead = FALSE; |
137 | signal(SIGCHLD, sigchld_handler); |
054d8535 |
138 | } |
139 | |
1709795f |
140 | return NULL; |
141 | } |
142 | |
143 | /* |
144 | * Called to send data down the pty. |
145 | */ |
146 | static int pty_send(char *buf, int len) |
147 | { |
054d8535 |
148 | while (len > 0) { |
149 | int ret = write(pty_master_fd, buf, len); |
150 | if (ret < 0) { |
151 | perror("write pty master"); |
152 | exit(1); |
153 | } |
154 | buf += ret; |
155 | len -= ret; |
156 | } |
1709795f |
157 | return 0; |
158 | } |
159 | |
160 | /* |
161 | * Called to query the current socket sendability status. |
162 | */ |
163 | static int pty_sendbuffer(void) |
164 | { |
165 | return 0; |
166 | } |
167 | |
168 | /* |
169 | * Called to set the size of the window |
170 | */ |
171 | static void pty_size(void) |
172 | { |
88e6b9ca |
173 | struct winsize size; |
174 | |
175 | size.ws_row = (unsigned short)rows; |
176 | size.ws_col = (unsigned short)cols; |
177 | ioctl(pty_master_fd, TIOCSWINSZ, (void *)&size); |
1709795f |
178 | return; |
179 | } |
180 | |
181 | /* |
182 | * Send special codes. |
183 | */ |
184 | static void pty_special(Telnet_Special code) |
185 | { |
186 | /* Do nothing! */ |
187 | return; |
188 | } |
189 | |
190 | static Socket pty_socket(void) |
191 | { |
192 | return NULL; /* shouldn't ever be needed */ |
193 | } |
194 | |
195 | static int pty_sendok(void) |
196 | { |
197 | return 1; |
198 | } |
199 | |
200 | static void pty_unthrottle(int backlog) |
201 | { |
202 | /* do nothing */ |
203 | } |
204 | |
205 | static int pty_ldisc(int option) |
206 | { |
207 | return 0; /* neither editing nor echoing */ |
208 | } |
209 | |
210 | static int pty_exitcode(void) |
211 | { |
212 | /* Shouldn't ever be required */ |
213 | return 0; |
214 | } |
215 | |
216 | Backend pty_backend = { |
217 | pty_init, |
218 | pty_send, |
219 | pty_sendbuffer, |
220 | pty_size, |
221 | pty_special, |
222 | pty_socket, |
223 | pty_exitcode, |
224 | pty_sendok, |
225 | pty_ldisc, |
226 | pty_unthrottle, |
227 | 1 |
228 | }; |