]>
Commit | Line | Data |
---|---|---|
1 | /* tty.cc | |
2 | ||
3 | This file is part of Cygwin. | |
4 | ||
5 | This software is a copyrighted work licensed under the terms of the | |
6 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
7 | details. */ | |
8 | ||
9 | #include "winsup.h" | |
10 | #include "miscfuncs.h" | |
11 | #include <unistd.h> | |
12 | #include <utmp.h> | |
13 | #include <sys/cygwin.h> | |
14 | #include "cygerrno.h" | |
15 | #include "security.h" | |
16 | #include "path.h" | |
17 | #include "fhandler.h" | |
18 | #include "dtable.h" | |
19 | #include "cygheap.h" | |
20 | #include "pinfo.h" | |
21 | #include "shared_info.h" | |
22 | ||
23 | HANDLE NO_COPY tty_list::mutex = NULL; | |
24 | ||
25 | extern "C" int | |
26 | getpt (void) | |
27 | { | |
28 | return open ("/dev/ptmx", O_RDWR | O_NOCTTY); | |
29 | } | |
30 | ||
31 | extern "C" int | |
32 | posix_openpt (int oflags) | |
33 | { | |
34 | return open ("/dev/ptmx", oflags); | |
35 | } | |
36 | ||
37 | extern "C" int | |
38 | grantpt (int fd) | |
39 | { | |
40 | cygheap_fdget cfd (fd); | |
41 | return cfd < 0 ? -1 : 0; | |
42 | } | |
43 | ||
44 | extern "C" int | |
45 | unlockpt (int fd) | |
46 | { | |
47 | cygheap_fdget cfd (fd); | |
48 | return cfd < 0 ? -1 : 0; | |
49 | } | |
50 | ||
51 | extern "C" int | |
52 | revoke (char *ttyname) | |
53 | { | |
54 | set_errno (ENOSYS); | |
55 | return -1; | |
56 | } | |
57 | ||
58 | extern "C" int | |
59 | ttyslot (void) | |
60 | { | |
61 | if (!CTTY_IS_VALID (myself->ctty) || iscons_dev (myself->ctty)) | |
62 | return -1; | |
63 | return device::minor (myself->ctty); | |
64 | } | |
65 | ||
66 | void | |
67 | tty_list::init_session () | |
68 | { | |
69 | char mutex_name[MAX_PATH]; | |
70 | char *name = shared_name (mutex_name, "tty_list::mutex", 0); | |
71 | ||
72 | /* tty_list::mutex is used while searching for a tty slot */ | |
73 | if (!(mutex = CreateMutex (&sec_all_nih, FALSE, name))) | |
74 | api_fatal ("can't create tty_list::mutex '%s', %E", name); | |
75 | ProtectHandle (mutex); | |
76 | } | |
77 | ||
78 | void | |
79 | tty::init_session () | |
80 | { | |
81 | if (!myself->cygstarted && NOTSTATE (myself, PID_CYGPARENT)) | |
82 | cygheap->fdtab.get_debugger_info (); | |
83 | } | |
84 | ||
85 | int | |
86 | tty_list::attach (int n) | |
87 | { | |
88 | int res; | |
89 | if (iscons_dev (n)) | |
90 | res = -1; | |
91 | else if (n != -1) | |
92 | res = connect (device::minor (n)); | |
93 | else | |
94 | res = -1; | |
95 | return res; | |
96 | } | |
97 | ||
98 | int | |
99 | tty_list::connect (int ttynum) | |
100 | { | |
101 | if (ttynum < 0 || ttynum >= NTTYS) | |
102 | { | |
103 | termios_printf ("ttynum (%d) out of range", ttynum); | |
104 | return -1; | |
105 | } | |
106 | if (!ttys[ttynum].exists ()) | |
107 | { | |
108 | termios_printf ("pty %d was not allocated", ttynum); | |
109 | set_errno (ENXIO); | |
110 | return -1; | |
111 | } | |
112 | ||
113 | return ttynum; | |
114 | } | |
115 | ||
116 | void | |
117 | tty_list::init () | |
118 | { | |
119 | for (int i = 0; i < NTTYS; i++) | |
120 | { | |
121 | ttys[i].init (); | |
122 | ttys[i].setntty (DEV_PTYS_MAJOR, i); | |
123 | } | |
124 | } | |
125 | ||
126 | /* Search for a free tty and allocate it. | |
127 | Return tty number or -1 if error. | |
128 | */ | |
129 | int | |
130 | tty_list::allocate (HANDLE& r, HANDLE& w) | |
131 | { | |
132 | lock_ttys here; | |
133 | int freetty = -1; | |
134 | ||
135 | tty *t = NULL; | |
136 | for (int i = 0; i < NTTYS; i++) | |
137 | if (ttys[i].not_allocated (r, w)) | |
138 | { | |
139 | t = ttys + i; | |
140 | t->init (); | |
141 | t->setsid (0); | |
142 | freetty = i; | |
143 | break; | |
144 | } | |
145 | ||
146 | if (freetty >= 0) | |
147 | termios_printf ("pty%d allocated", freetty); | |
148 | else | |
149 | { | |
150 | system_printf ("No pty allocated"); | |
151 | r = w = NULL; | |
152 | } | |
153 | ||
154 | return freetty; | |
155 | } | |
156 | ||
157 | bool | |
158 | tty::not_allocated (HANDLE& r, HANDLE& w) | |
159 | { | |
160 | /* Attempt to open the from-master side of the tty. If it is accessible | |
161 | then it exists although we may not have privileges to actually use it. */ | |
162 | char pipename[sizeof("ptyNNNN-from-master")]; | |
163 | __small_sprintf (pipename, "pty%d-from-master", get_minor ()); | |
164 | /* fhandler_pipe::create returns 0 when creation succeeds */ | |
165 | return fhandler_pipe::create (&sec_none, &r, &w, | |
166 | fhandler_pty_common::pipesize, pipename, | |
167 | 0) == 0; | |
168 | } | |
169 | ||
170 | bool | |
171 | tty::exists () | |
172 | { | |
173 | HANDLE r, w; | |
174 | bool res; | |
175 | if (!not_allocated (r, w)) | |
176 | res = true; | |
177 | ||
178 | else | |
179 | { | |
180 | /* Handles are left open when not_allocated finds a non-open "tty" */ | |
181 | CloseHandle (r); | |
182 | CloseHandle (w); | |
183 | res = false; | |
184 | } | |
185 | debug_printf ("exists %d", res); | |
186 | return res; | |
187 | } | |
188 | ||
189 | bool | |
190 | tty::slave_alive () | |
191 | { | |
192 | HANDLE ev; | |
193 | if ((ev = open_inuse (READ_CONTROL))) | |
194 | CloseHandle (ev); | |
195 | return ev != NULL; | |
196 | } | |
197 | ||
198 | HANDLE | |
199 | tty::open_mutex (const char *mutex, ACCESS_MASK access) | |
200 | { | |
201 | char buf[MAX_PATH]; | |
202 | shared_name (buf, mutex, get_minor ()); | |
203 | return OpenMutex (access, TRUE, buf); | |
204 | } | |
205 | ||
206 | HANDLE | |
207 | tty::open_inuse (ACCESS_MASK access) | |
208 | { | |
209 | char buf[MAX_PATH]; | |
210 | shared_name (buf, TTY_SLAVE_ALIVE, get_minor ()); | |
211 | return OpenEvent (access, FALSE, buf); | |
212 | } | |
213 | ||
214 | HANDLE | |
215 | tty::create_inuse (PSECURITY_ATTRIBUTES sa) | |
216 | { | |
217 | HANDLE h; | |
218 | char buf[MAX_PATH]; | |
219 | ||
220 | shared_name (buf, TTY_SLAVE_ALIVE, get_minor ()); | |
221 | h = CreateEvent (sa, TRUE, FALSE, buf); | |
222 | termios_printf ("%s %p", buf, h); | |
223 | if (!h) | |
224 | termios_printf ("couldn't open inuse event %s, %E", buf); | |
225 | return h; | |
226 | } | |
227 | ||
228 | void | |
229 | tty::init () | |
230 | { | |
231 | output_stopped = 0; | |
232 | setsid (0); | |
233 | pgid = 0; | |
234 | was_opened = false; | |
235 | master_pid = 0; | |
236 | is_console = false; | |
237 | column = 0; | |
238 | pcon_activated = false; | |
239 | switch_to_nat_pipe = false; | |
240 | nat_pipe_owner_pid = 0; | |
241 | term_code_page = 0; | |
242 | fwd_last_time = 0; | |
243 | fwd_not_empty = false; | |
244 | pcon_start = false; | |
245 | pcon_start_pid = 0; | |
246 | pcon_cap_checked = false; | |
247 | has_csi6n = false; | |
248 | need_invisible_console = false; | |
249 | invisible_console_pid = 0; | |
250 | previous_code_page = 0; | |
251 | previous_output_code_page = 0; | |
252 | master_is_running_as_service = false; | |
253 | req_xfer_input = false; | |
254 | pty_input_state = to_cyg; | |
255 | last_sig = 0; | |
256 | mask_flusho = false; | |
257 | discard_input = false; | |
258 | stop_fwd_thread = false; | |
259 | } | |
260 | ||
261 | HANDLE | |
262 | tty::get_event (const char *fmt, PSECURITY_ATTRIBUTES sa, BOOL manual_reset) | |
263 | { | |
264 | HANDLE hev; | |
265 | char buf[MAX_PATH]; | |
266 | ||
267 | shared_name (buf, fmt, get_minor ()); | |
268 | if (!sa) | |
269 | sa = &sec_all; | |
270 | if (!(hev = CreateEvent (sa, manual_reset, FALSE, buf))) | |
271 | { | |
272 | termios_printf ("couldn't create %s", buf); | |
273 | set_errno (ENOENT); /* FIXME this can't be the right errno */ | |
274 | return NULL; | |
275 | } | |
276 | ||
277 | termios_printf ("created event %s", buf); | |
278 | return hev; | |
279 | } | |
280 | ||
281 | lock_ttys::lock_ttys (DWORD howlong): release_me (true) | |
282 | { | |
283 | if (WaitForSingleObject (tty_list::mutex, howlong) == WAIT_FAILED) | |
284 | { | |
285 | termios_printf ("WFSO for mutex %p failed, %E", tty_list::mutex); | |
286 | release_me = false; | |
287 | } | |
288 | } | |
289 | ||
290 | void | |
291 | lock_ttys::release () | |
292 | { | |
293 | ReleaseMutex (tty_list::mutex); | |
294 | } | |
295 | ||
296 | const char * | |
297 | tty_min::ttyname () | |
298 | { | |
299 | device d; | |
300 | d.parse (ntty); | |
301 | return d.name (); | |
302 | } | |
303 | ||
304 | extern DWORD mutex_timeout; /* defined in fhandler_termios.cc */ | |
305 | ||
306 | void | |
307 | tty_min::setpgid (int pid) | |
308 | { | |
309 | if (::cygheap->ctty) | |
310 | ::cygheap->ctty->setpgid_aux (pid); | |
311 | ||
312 | pgid = pid; | |
313 | } | |
314 | ||
315 | void | |
316 | tty::wait_fwd () | |
317 | { | |
318 | /* The forwarding in pseudo console sometimes stops for | |
319 | 16-32 msec even if it already has data to transfer. | |
320 | If the time without transfer exceeds 32 msec, the | |
321 | forwarding is supposed to be finished. fwd_last_time | |
322 | is reset to GetTickCount64() in pty master forwarding | |
323 | thread when the last data is transfered. */ | |
324 | const ULONGLONG sleep_in_nat_pipe = 16; | |
325 | const ULONGLONG time_to_wait = sleep_in_nat_pipe * 2 + 1/* margine */; | |
326 | ULONGLONG elapsed = 0; | |
327 | while (fwd_not_empty | |
328 | || (elapsed = GetTickCount64 () - fwd_last_time) < time_to_wait) | |
329 | { | |
330 | int tw = fwd_not_empty ? 10 : (time_to_wait - elapsed); | |
331 | cygwait (tw); | |
332 | } | |
333 | } | |
334 | ||
335 | bool | |
336 | tty::nat_fg (pid_t pgid) | |
337 | { | |
338 | /* Check if the terminal pgid matches with the pgid of the | |
339 | non-cygwin process. */ | |
340 | winpids pids ((DWORD) 0); | |
341 | for (unsigned i = 0; i < pids.npids; i++) | |
342 | { | |
343 | _pinfo *p = pids[i]; | |
344 | if (p->ctty == ntty && p->pgid == pgid | |
345 | && ((p->process_state & PID_NOTCYGWIN) | |
346 | /* Below is true for GDB with non-cygwin inferior */ | |
347 | || p->exec_dwProcessId == p->dwProcessId)) | |
348 | return true; | |
349 | } | |
350 | if (pgid > MAX_PID) | |
351 | return true; | |
352 | return false; | |
353 | } |