Fix a controlling-terminal bug reported by Anthony Heading: Cygwin
[sgt/putty] / contrib / cygtermd / pty.c
CommitLineData
5e4a475d 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
23static char ptyname[FILENAME_MAX];
24int master = -1;
25
26void 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
48void 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
60int 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
b359e6cc 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) {
5e4a475d 134#ifdef TIOCSCTTY
b359e6cc 135 ioctl(fd, TIOCSCTTY, &i);
5e4a475d 136#endif
b359e6cc 137 close(fd);
138 } else {
139 perror("slave pty: open");
140 exit(127);
141 }
5e4a475d 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}