blob: 141ef7d35ad694a9770574c022dea920a4835e81 [file] [log] [blame]
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001/*
2
3pty.c
4
5Author: Tatu Ylonen <ylo@cs.hut.fi>
6
7Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
8 All rights reserved
9
10Created: Fri Mar 17 04:37:25 1995 ylo
11
12Allocating a pseudo-terminal, and making it the controlling tty.
13
14*/
15
16#include "includes.h"
Damien Miller356a0b01999-11-08 15:30:59 +110017RCSID("$Id: pty.c,v 1.2 1999/11/08 04:30:59 damien Exp $");
18
19#ifdef HAVE_PTY_H
20/* Unfortunate namespace collision */
21#include <pty.h>
22#endif /* HAVE_PTY_H */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100023
24#include "pty.h"
25#include "ssh.h"
26
27/* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */
28#if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY)
29#undef HAVE_DEV_PTMX
30#endif
31
32#ifndef O_NOCTTY
33#define O_NOCTTY 0
34#endif
35
36/* Allocates and opens a pty. Returns 0 if no pty could be allocated,
37 or nonzero if a pty was successfully allocated. On success, open file
38 descriptors for the pty and tty sides and the name of the tty side are
39 returned (the buffer must be able to hold at least 64 characters). */
40
41int pty_allocate(int *ptyfd, int *ttyfd, char *namebuf)
42{
43#ifdef HAVE_OPENPTY
44
45 /* openpty(3) exists in OSF/1 and some other os'es */
46
47 int i;
48
49 i = openpty(ptyfd, ttyfd, namebuf, NULL, NULL);
50
51 if (i < 0)
52 {
53 error("openpty: %.100s", strerror(errno));
54 return 0;
55 }
56
57 return 1;
58
59#else /* HAVE_OPENPTY */
60#ifdef HAVE__GETPTY
61
62 /* _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more
63 pty's automagically when needed */
64
65 char *slave;
66
67 slave = _getpty(ptyfd, O_RDWR, 0622, 0);
68 if (slave == NULL)
69 {
70 error("_getpty: %.100s", strerror(errno));
71 return 0;
72 }
73 strcpy(namebuf, slave);
74 /* Open the slave side. */
75 *ttyfd = open(namebuf, O_RDWR|O_NOCTTY);
76 if (*ttyfd < 0)
77 {
78 error("%.200s: %.100s", namebuf, strerror(errno));
79 close(*ptyfd);
80 return 0;
81 }
82 return 1;
83
84#else /* HAVE__GETPTY */
85#ifdef HAVE_DEV_PTMX
86 /* This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 also has
87 bsd-style ptys, but they simply do not work.) */
88
89 int ptm;
90 char *pts;
91
92 ptm = open("/dev/ptmx", O_RDWR|O_NOCTTY);
93 if (ptm < 0)
94 {
95 error("/dev/ptmx: %.100s", strerror(errno));
96 return 0;
97 }
98 if (grantpt(ptm) < 0)
99 {
100 error("grantpt: %.100s", strerror(errno));
101 return 0;
102 }
103 if (unlockpt(ptm) < 0)
104 {
105 error("unlockpt: %.100s", strerror(errno));
106 return 0;
107 }
108 pts = ptsname(ptm);
109 if (pts == NULL)
110 error("Slave pty side name could not be obtained.");
111 strcpy(namebuf, pts);
112 *ptyfd = ptm;
113
114 /* Open the slave side. */
115 *ttyfd = open(namebuf, O_RDWR|O_NOCTTY);
116 if (*ttyfd < 0)
117 {
118 error("%.100s: %.100s", namebuf, strerror(errno));
119 close(*ptyfd);
120 return 0;
121 }
122 /* Push the appropriate streams modules, as described in Solaris pts(7). */
123 if (ioctl(*ttyfd, I_PUSH, "ptem") < 0)
124 error("ioctl I_PUSH ptem: %.100s", strerror(errno));
125 if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0)
126 error("ioctl I_PUSH ldterm: %.100s", strerror(errno));
127 if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0)
128 error("ioctl I_PUSH ttcompat: %.100s", strerror(errno));
129 return 1;
130
131#else /* HAVE_DEV_PTMX */
132#ifdef HAVE_DEV_PTS_AND_PTC
133
134 /* AIX-style pty code. */
135
136 const char *name;
137
138 *ptyfd = open("/dev/ptc", O_RDWR|O_NOCTTY);
139 if (*ptyfd < 0)
140 {
141 error("Could not open /dev/ptc: %.100s", strerror(errno));
142 return 0;
143 }
144 name = ttyname(*ptyfd);
145 if (!name)
146 fatal("Open of /dev/ptc returns device for which ttyname fails.");
147 strcpy(namebuf, name);
148 *ttyfd = open(name, O_RDWR|O_NOCTTY);
149 if (*ttyfd < 0)
150 {
151 error("Could not open pty slave side %.100s: %.100s",
152 name, strerror(errno));
153 close(*ptyfd);
154 return 0;
155 }
156 return 1;
157
158#else /* HAVE_DEV_PTS_AND_PTC */
159 /* BSD-style pty code. */
160
161 char buf[64];
162 int i;
163 const char *ptymajors =
164 "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ";
165 const char *ptyminors = "0123456789abcdef";
166 int num_minors = strlen(ptyminors);
167 int num_ptys = strlen(ptymajors) * num_minors;
168
169 for (i = 0; i < num_ptys; i++)
170 {
171 snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors],
172 ptyminors[i % num_minors]);
173 *ptyfd = open(buf, O_RDWR|O_NOCTTY);
174 if (*ptyfd < 0)
175 continue;
176 snprintf(namebuf, sizeof buf, "/dev/tty%c%c", ptymajors[i / num_minors],
177 ptyminors[i % num_minors]);
178
179 /* Open the slave side. */
180 *ttyfd = open(namebuf, O_RDWR|O_NOCTTY);
181 if (*ttyfd < 0)
182 {
183 error("%.100s: %.100s", namebuf, strerror(errno));
184 close(*ptyfd);
185 return 0;
186 }
187 return 1;
188 }
189 return 0;
190#endif /* HAVE_DEV_PTS_AND_PTC */
191#endif /* HAVE_DEV_PTMX */
192#endif /* HAVE__GETPTY */
193#endif /* HAVE_OPENPTY */
194}
195
196/* Releases the tty. Its ownership is returned to root, and permissions to
197 0666. */
198
199void pty_release(const char *ttyname)
200{
201 if (chown(ttyname, (uid_t)0, (gid_t)0) < 0)
202 debug("chown %.100s 0 0 failed: %.100s", ttyname, strerror(errno));
203 if (chmod(ttyname, (mode_t)0666) < 0)
204 debug("chmod %.100s 0666 failed: %.100s", ttyname, strerror(errno));
205}
206
207/* Makes the tty the processes controlling tty and sets it to sane modes. */
208
209void pty_make_controlling_tty(int *ttyfd, const char *ttyname)
210{
211 int fd;
212
213 /* First disconnect from the old controlling tty. */
214#ifdef TIOCNOTTY
215 fd = open("/dev/tty", O_RDWR|O_NOCTTY);
216 if (fd >= 0)
217 {
218 (void)ioctl(fd, TIOCNOTTY, NULL);
219 close(fd);
220 }
221#endif /* TIOCNOTTY */
222 if (setsid() < 0)
223 error("setsid: %.100s", strerror(errno));
224
225 /* Verify that we are successfully disconnected from the controlling tty. */
226 fd = open("/dev/tty", O_RDWR|O_NOCTTY);
227 if (fd >= 0)
228 {
229 error("Failed to disconnect from controlling tty.");
230 close(fd);
231 }
232
233 /* Make it our controlling tty. */
234#ifdef TIOCSCTTY
235 debug("Setting controlling tty using TIOCSCTTY.");
236 /* We ignore errors from this, because HPSUX defines TIOCSCTTY, but returns
237 EINVAL with these arguments, and there is absolutely no documentation. */
238 ioctl(*ttyfd, TIOCSCTTY, NULL);
239#endif /* TIOCSCTTY */
240 fd = open(ttyname, O_RDWR);
241 if (fd < 0)
242 error("%.100s: %.100s", ttyname, strerror(errno));
243 else
244 close(fd);
245
246 /* Verify that we now have a controlling tty. */
247 fd = open("/dev/tty", O_WRONLY);
248 if (fd < 0)
249 error("open /dev/tty failed - could not set controlling tty: %.100s",
250 strerror(errno));
251 else
252 {
253 close(fd);
254 }
255}
256
257/* Changes the window size associated with the pty. */
258
259void pty_change_window_size(int ptyfd, int row, int col,
260 int xpixel, int ypixel)
261{
262 struct winsize w;
263 w.ws_row = row;
264 w.ws_col = col;
265 w.ws_xpixel = xpixel;
266 w.ws_ypixel = ypixel;
267 (void)ioctl(ptyfd, TIOCSWINSZ, &w);
268}
269