Deal with the warnings generated when passing a pointer-to-enum to
[u/mdw/putty] / unix / pty.c
CommitLineData
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 26int pty_master_fd;
a0e16eb1 27static int pty_child_pid;
28static sig_atomic_t pty_child_dead;
6169c758 29char **pty_argv;
054d8535 30
a0e16eb1 31int pty_child_is_dead(void)
32{
33 return pty_child_dead;
34}
35
1709795f 36static void pty_size(void);
37
a0e16eb1 38static 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 */
55static 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 */
188static 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 */
205static int pty_sendbuffer(void)
206{
207 return 0;
208}
209
210/*
211 * Called to set the size of the window
212 */
213static 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 */
226static void pty_special(Telnet_Special code)
227{
228 /* Do nothing! */
229 return;
230}
231
232static Socket pty_socket(void)
233{
234 return NULL; /* shouldn't ever be needed */
235}
236
237static int pty_sendok(void)
238{
239 return 1;
240}
241
242static void pty_unthrottle(int backlog)
243{
244 /* do nothing */
245}
246
247static int pty_ldisc(int option)
248{
249 return 0; /* neither editing nor echoing */
250}
251
252static int pty_exitcode(void)
253{
254 /* Shouldn't ever be required */
255 return 0;
256}
257
258Backend 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};