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> |
be4f86de |
12 | #include <grp.h> |
a0e16eb1 |
13 | #include <sys/types.h> |
14 | #include <sys/wait.h> |
88e6b9ca |
15 | #include <sys/ioctl.h> |
1709795f |
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 | |
054d8535 |
26 | int pty_master_fd; |
a0e16eb1 |
27 | static int pty_child_pid; |
28 | static sig_atomic_t pty_child_dead; |
6169c758 |
29 | char **pty_argv; |
054d8535 |
30 | |
a0e16eb1 |
31 | int pty_child_is_dead(void) |
32 | { |
33 | return pty_child_dead; |
34 | } |
35 | |
1709795f |
36 | static void pty_size(void); |
37 | |
a0e16eb1 |
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 | |
1709795f |
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 | { |
054d8535 |
57 | int slavefd; |
58 | char name[FILENAME_MAX]; |
d37c2d81 |
59 | pid_t pid, pgrp; |
054d8535 |
60 | |
be4f86de |
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 |
054d8535 |
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); |
be4f86de |
108 | #endif |
054d8535 |
109 | |
054d8535 |
110 | /* |
111 | * Fork and execute the command. |
112 | */ |
113 | pid = fork(); |
114 | if (pid < 0) { |
115 | perror("fork"); |
88e6b9ca |
116 | exit(1); |
054d8535 |
117 | } |
118 | |
119 | if (pid == 0) { |
120 | int i; |
121 | /* |
122 | * We are the child. |
123 | */ |
d37c2d81 |
124 | |
125 | slavefd = open(name, O_RDWR); |
126 | if (slavefd < 0) { |
127 | perror("slave pty: open"); |
128 | exit(1); |
129 | } |
130 | |
be4f86de |
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 | |
054d8535 |
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(); |
d37c2d81 |
149 | ioctl(slavefd, TIOCSCTTY, 1); |
150 | pgrp = getpid(); |
151 | tcsetpgrp(slavefd, pgrp); |
152 | setpgrp(); |
153 | close(open(name, O_WRONLY, 0)); |
054d8535 |
154 | setpgrp(); |
be4f86de |
155 | /* In case we were setgid-utmp or setuid-root, drop privs. */ |
156 | setgid(getgid()); |
157 | setuid(getuid()); |
054d8535 |
158 | /* Close everything _else_, for tidiness. */ |
159 | for (i = 3; i < 1024; i++) |
160 | close(i); |
fe9548aa |
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 | } |
6169c758 |
166 | if (pty_argv) |
167 | execvp(pty_argv[0], pty_argv); |
168 | else |
169 | execl(getenv("SHELL"), getenv("SHELL"), NULL); |
054d8535 |
170 | /* |
171 | * If we're here, exec has gone badly foom. |
172 | */ |
173 | perror("exec"); |
174 | exit(127); |
175 | } else { |
176 | close(slavefd); |
a0e16eb1 |
177 | pty_child_pid = pid; |
178 | pty_child_dead = FALSE; |
179 | signal(SIGCHLD, sigchld_handler); |
054d8535 |
180 | } |
181 | |
1709795f |
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 | { |
054d8535 |
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 | } |
1709795f |
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 | { |
88e6b9ca |
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); |
1709795f |
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 | }; |