]>
Commit | Line | Data |
---|---|---|
1fd5e000 CF |
1 | /* fhandler_tty.cc |
2 | ||
90430595 | 3 | Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, |
a7d2cc16 | 4 | 2006, 2007, 2008 Red Hat, Inc. |
1fd5e000 CF |
5 | |
6 | This file is part of Cygwin. | |
7 | ||
8 | This software is a copyrighted work licensed under the terms of the | |
9 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
10 | details. */ | |
11 | ||
4c8d72de | 12 | #include "winsup.h" |
1fd5e000 | 13 | #include <stdlib.h> |
6258d96a | 14 | #include <cygwin/kd.h> |
bccd5e0d | 15 | #include "cygerrno.h" |
6b91b8d5 | 16 | #include "security.h" |
47063f00 | 17 | #include "path.h" |
7ac61736 | 18 | #include "fhandler.h" |
e2ebe117 | 19 | #include "dtable.h" |
bccd5e0d | 20 | #include "sigproc.h" |
84cf3d22 | 21 | #include "pinfo.h" |
b0e82b74 | 22 | #include "cygheap.h" |
29ac7f89 | 23 | #include "shared_info.h" |
e217832c | 24 | #include "cygserver.h" |
b6bd7037 | 25 | #include "cygthread.h" |
3378bdfc | 26 | #include "child_info.h" |
1fd5e000 | 27 | |
3378bdfc | 28 | /* tty master stuff */ |
1fd5e000 CF |
29 | |
30 | fhandler_tty_master NO_COPY *tty_master; | |
31 | ||
32 | static DWORD WINAPI process_input (void *); // Input queue thread | |
33 | static DWORD WINAPI process_output (void *); // Output queue thread | |
34 | static DWORD WINAPI process_ioctl (void *); // Ioctl requests thread | |
35 | ||
7ac61736 CF |
36 | fhandler_tty_master::fhandler_tty_master () |
37 | : fhandler_pty_master (), console (NULL) | |
38 | { | |
39 | } | |
40 | ||
41 | int | |
42 | fhandler_tty_slave::get_unit () | |
1fd5e000 | 43 | { |
7ac61736 | 44 | return dev () == FH_TTY ? myself->ctty : dev ().minor; |
1fd5e000 CF |
45 | } |
46 | ||
66dcfc44 CF |
47 | void |
48 | fhandler_tty_master::set_winsize (bool sendSIGWINCH) | |
49 | { | |
50 | winsize w; | |
51 | console->ioctl (TIOCGWINSZ, &w); | |
52 | get_ttyp ()->winsize = w; | |
53 | if (sendSIGWINCH) | |
54 | tc->kill_pgrp (SIGWINCH); | |
55 | } | |
56 | ||
1fd5e000 | 57 | int |
7ac61736 | 58 | fhandler_tty_master::init () |
1fd5e000 | 59 | { |
0e1ba888 | 60 | termios_printf ("Creating master for tty%d", get_unit ()); |
1fd5e000 CF |
61 | |
62 | if (init_console ()) | |
63 | { | |
64 | termios_printf ("can't create fhandler"); | |
65 | return -1; | |
66 | } | |
67 | ||
68 | termios ti; | |
69 | memset (&ti, 0, sizeof (ti)); | |
70 | console->tcsetattr (0, &ti); | |
71 | ||
c76ca047 | 72 | if (!setup (false)) |
3378bdfc | 73 | return 1; |
1fd5e000 | 74 | |
66dcfc44 CF |
75 | set_winsize (false); |
76 | ||
1df3fbe2 | 77 | set_close_on_exec (true); |
78ace8a7 | 78 | |
b6bd7037 | 79 | cygthread *h; |
267e201d | 80 | h = new cygthread (process_input, 0, cygself, "ttyin"); |
c4ec64d7 CF |
81 | h->SetThreadPriority (THREAD_PRIORITY_HIGHEST); |
82 | h->zap_h (); | |
1fd5e000 | 83 | |
267e201d | 84 | h = new cygthread (process_ioctl, 0, cygself, "ttyioctl"); |
c4ec64d7 CF |
85 | h->SetThreadPriority (THREAD_PRIORITY_HIGHEST); |
86 | h->zap_h (); | |
1fd5e000 | 87 | |
267e201d | 88 | h = new cygthread (process_output, 0, cygself, "ttyout"); |
c4ec64d7 CF |
89 | h->SetThreadPriority (THREAD_PRIORITY_HIGHEST); |
90 | h->zap_h (); | |
1fd5e000 CF |
91 | |
92 | return 0; | |
93 | } | |
94 | ||
95 | #ifdef DEBUGGING | |
96 | static class mutex_stack | |
97 | { | |
98 | public: | |
99 | const char *fn; | |
100 | int ln; | |
101 | const char *tname; | |
102 | } ostack[100]; | |
103 | ||
08b78edf | 104 | static int osi; |
1fd5e000 CF |
105 | #endif /*DEBUGGING*/ |
106 | ||
107 | DWORD | |
108 | fhandler_tty_common::__acquire_output_mutex (const char *fn, int ln, | |
109 | DWORD ms) | |
110 | { | |
5d970405 | 111 | if (strace.active ()) |
20d7f758 | 112 | strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: waiting %d ms", ln, ms); |
1fd5e000 CF |
113 | DWORD res = WaitForSingleObject (output_mutex, ms); |
114 | if (res == WAIT_OBJECT_0) | |
115 | { | |
af1dc7cc | 116 | #ifndef DEBUGGING |
b0cc1689 | 117 | if (strace.active ()) |
20d7f758 | 118 | strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: acquired", ln, res); |
af1dc7cc | 119 | #else |
1fd5e000 CF |
120 | ostack[osi].fn = fn; |
121 | ostack[osi].ln = ln; | |
b6bd7037 | 122 | ostack[osi].tname = cygthread::name (); |
1fd5e000 CF |
123 | termios_printf ("acquired for %s:%d, osi %d", fn, ln, osi); |
124 | osi++; | |
125 | #endif | |
126 | } | |
1fd5e000 CF |
127 | return res; |
128 | } | |
129 | ||
130 | void | |
131 | fhandler_tty_common::__release_output_mutex (const char *fn, int ln) | |
132 | { | |
133 | if (ReleaseMutex (output_mutex)) | |
134 | { | |
af1dc7cc | 135 | #ifndef DEBUGGING |
b0cc1689 | 136 | if (strace.active ()) |
20d7f758 | 137 | strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex released", ln); |
af1dc7cc | 138 | #else |
1fd5e000 CF |
139 | if (osi > 0) |
140 | osi--; | |
141 | termios_printf ("released at %s:%d, osi %d", fn, ln, osi); | |
2116a175 | 142 | termios_printf (" for %s:%d (%s)", ostack[osi].fn, ostack[osi].ln, ostack[osi].tname); |
1fd5e000 CF |
143 | ostack[osi].ln = -ln; |
144 | #endif | |
145 | } | |
6813f009 CF |
146 | #ifdef DEBUGGING |
147 | else if (osi > 0) | |
148 | { | |
149 | system_printf ("couldn't release output mutex but we seem to own it, %E"); | |
150 | try_to_debug (); | |
151 | } | |
152 | #endif | |
1fd5e000 CF |
153 | } |
154 | ||
1fd5e000 CF |
155 | /* Process tty input. */ |
156 | ||
157 | void | |
158 | fhandler_pty_master::doecho (const void *str, DWORD len) | |
159 | { | |
160 | acquire_output_mutex (INFINITE); | |
578e142a CF |
161 | if (!WriteFile (to_master, str, len, &len, NULL)) |
162 | termios_printf ("Write to %p failed, %E", to_master); | |
1fd5e000 CF |
163 | // WaitForSingleObject (output_done_event, INFINITE); |
164 | release_output_mutex (); | |
165 | } | |
166 | ||
167 | int | |
168 | fhandler_pty_master::accept_input () | |
169 | { | |
b2be3149 CF |
170 | DWORD bytes_left; |
171 | int ret = 1; | |
306c4b67 | 172 | |
0c55f6ed | 173 | WaitForSingleObject (input_mutex, INFINITE); |
1fd5e000 | 174 | |
b2be3149 | 175 | bytes_left = eat_readahead (-1); |
1fd5e000 | 176 | |
b2be3149 | 177 | if (!bytes_left) |
1fd5e000 | 178 | { |
b2be3149 CF |
179 | termios_printf ("sending EOF to slave"); |
180 | get_ttyp ()->read_retval = 0; | |
181 | } | |
182 | else | |
183 | { | |
184 | char *p = rabuf; | |
185 | DWORD rc; | |
186 | DWORD written = 0; | |
4f96ae69 | 187 | |
b2be3149 CF |
188 | termios_printf ("about to write %d chars to slave", bytes_left); |
189 | rc = WriteFile (get_output_handle (), p, bytes_left, &written, NULL); | |
190 | if (!rc) | |
191 | { | |
192 | debug_printf ("error writing to pipe %E"); | |
193 | get_ttyp ()->read_retval = -1; | |
388aa994 | 194 | ret = -1; |
b2be3149 CF |
195 | } |
196 | else | |
197 | { | |
198 | get_ttyp ()->read_retval = 1; | |
a069f560 ED |
199 | p += written; |
200 | bytes_left -= written; | |
201 | if (bytes_left > 0) | |
202 | { | |
203 | debug_printf ("to_slave pipe is full"); | |
b2be3149 CF |
204 | puts_readahead (p, bytes_left); |
205 | ret = 0; | |
a069f560 ED |
206 | } |
207 | } | |
1fd5e000 | 208 | } |
b2be3149 | 209 | |
306c4b67 ED |
210 | SetEvent (input_available_event); |
211 | ReleaseMutex (input_mutex); | |
b2be3149 | 212 | return ret; |
1fd5e000 CF |
213 | } |
214 | ||
215 | static DWORD WINAPI | |
9cec3d45 | 216 | process_input (void *) |
1fd5e000 CF |
217 | { |
218 | char rawbuf[INP_BUFFER_SIZE]; | |
219 | ||
220 | while (1) | |
221 | { | |
8bce0d72 CF |
222 | size_t nraw = INP_BUFFER_SIZE; |
223 | tty_master->console->read ((void *) rawbuf, nraw); | |
7a44ba05 CF |
224 | if (tty_master->line_edit (rawbuf, nraw, tty_master->get_ttyp ()->ti) |
225 | == line_edit_signalled) | |
226 | tty_master->console->eat_readahead (-1); | |
1fd5e000 CF |
227 | } |
228 | } | |
229 | ||
35f879a6 | 230 | bool |
1fd5e000 CF |
231 | fhandler_pty_master::hit_eof () |
232 | { | |
233 | if (get_ttyp ()->was_opened && !get_ttyp ()->slave_alive ()) | |
234 | { | |
235 | /* We have the only remaining open handle to this pty, and | |
236 | the slave pty has been opened at least once. We treat | |
237 | this as EOF. */ | |
238 | termios_printf ("all other handles closed"); | |
239 | return 1; | |
240 | } | |
241 | return 0; | |
242 | } | |
243 | ||
244 | /* Process tty output requests */ | |
245 | ||
246 | int | |
3f0b4935 | 247 | fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on) |
1fd5e000 CF |
248 | { |
249 | size_t rlen; | |
3f0b4935 | 250 | char outbuf[OUT_BUFFER_SIZE + 1]; |
1fd5e000 CF |
251 | DWORD n; |
252 | int column = 0; | |
774ea162 | 253 | int rc = 0; |
1fd5e000 CF |
254 | |
255 | if (len == 0) | |
774ea162 | 256 | goto out; |
1fd5e000 | 257 | |
3f0b4935 | 258 | if (need_nl) |
1fd5e000 | 259 | { |
3f0b4935 CF |
260 | /* We need to return a left over \n character, resulting from |
261 | \r\n conversion. Note that we already checked for FLUSHO and | |
b98ebf54 | 262 | output_stopped at the time that we read the character, so we |
3f0b4935 | 263 | don't check again here. */ |
41946df6 CV |
264 | if (buf) |
265 | buf[0] = '\n'; | |
3f0b4935 | 266 | need_nl = 0; |
14323f6a | 267 | rc = 1; |
3f0b4935 CF |
268 | goto out; |
269 | } | |
1fd5e000 | 270 | |
3f0b4935 CF |
271 | |
272 | for (;;) | |
273 | { | |
774ea162 CF |
274 | /* Set RLEN to the number of bytes to read from the pipe. */ |
275 | rlen = len; | |
276 | if (get_ttyp ()->ti.c_oflag & OPOST && get_ttyp ()->ti.c_oflag & ONLCR) | |
277 | { | |
278 | /* We are going to expand \n to \r\n, so don't read more than | |
279 | half of the number of bytes requested. */ | |
280 | rlen /= 2; | |
281 | if (rlen == 0) | |
282 | rlen = 1; | |
283 | } | |
284 | if (rlen > sizeof outbuf) | |
285 | rlen = sizeof outbuf; | |
1fd5e000 | 286 | |
774ea162 | 287 | HANDLE handle = get_io_handle (); |
1fd5e000 | 288 | |
3f0b4935 CF |
289 | n = 0; // get_readahead_into_buffer (outbuf, len); |
290 | if (!n) | |
1fd5e000 | 291 | { |
3f0b4935 CF |
292 | /* Doing a busy wait like this is quite inefficient, but nothing |
293 | else seems to work completely. Windows should provide some sort | |
294 | of overlapped I/O for pipes, or something, but it doesn't. */ | |
295 | while (1) | |
296 | { | |
297 | if (!PeekNamedPipe (handle, NULL, 0, NULL, &n, NULL)) | |
298 | goto err; | |
299 | if (n > 0) | |
300 | break; | |
301 | if (hit_eof ()) | |
302 | goto out; | |
41946df6 CV |
303 | /* DISCARD (FLUSHO) and tcflush can finish here. */ |
304 | if (n == 0 && (get_ttyp ()->ti.c_lflag & FLUSHO || !buf)) | |
e3778517 | 305 | goto out; |
5fd12fb0 | 306 | if (n == 0 && is_nonblocking ()) |
3f0b4935 CF |
307 | { |
308 | set_errno (EAGAIN); | |
309 | rc = -1; | |
310 | break; | |
311 | } | |
312 | ||
b263d1d8 | 313 | Sleep (10); |
3f0b4935 CF |
314 | } |
315 | ||
316 | if (ReadFile (handle, outbuf, rlen, &n, NULL) == FALSE) | |
774ea162 | 317 | goto err; |
1fd5e000 | 318 | } |
1fd5e000 | 319 | |
3f0b4935 | 320 | termios_printf ("bytes read %u", n); |
29ac7f89 CF |
321 | get_ttyp ()->write_error = 0; |
322 | if (output_done_event != NULL) | |
323 | SetEvent (output_done_event); | |
1fd5e000 | 324 | |
41946df6 | 325 | if (get_ttyp ()->ti.c_lflag & FLUSHO || !buf) |
29ac7f89 | 326 | continue; |
1fd5e000 | 327 | |
3f0b4935 CF |
328 | char *optr; |
329 | optr = buf; | |
330 | if (pktmode_on) | |
331 | *optr++ = TIOCPKT_DATA; | |
332 | ||
774ea162 | 333 | if (!(get_ttyp ()->ti.c_oflag & OPOST)) // post-process output |
1fd5e000 | 334 | { |
3f0b4935 CF |
335 | memcpy (optr, outbuf, n); |
336 | optr += n; | |
774ea162 CF |
337 | } |
338 | else // raw output mode | |
339 | { | |
3f0b4935 | 340 | char *iptr = outbuf; |
774ea162 CF |
341 | |
342 | while (n--) | |
1fd5e000 | 343 | { |
774ea162 | 344 | switch (*iptr) |
1fd5e000 | 345 | { |
774ea162 CF |
346 | case '\r': |
347 | if ((get_ttyp ()->ti.c_oflag & ONOCR) && column == 0) | |
348 | { | |
349 | iptr++; | |
350 | continue; | |
351 | } | |
352 | if (get_ttyp ()->ti.c_oflag & OCRNL) | |
353 | *iptr = '\n'; | |
354 | else | |
355 | column = 0; | |
356 | break; | |
357 | case '\n': | |
358 | if (get_ttyp ()->ti.c_oflag & ONLCR) | |
359 | { | |
360 | *optr++ = '\r'; | |
361 | column = 0; | |
362 | } | |
363 | if (get_ttyp ()->ti.c_oflag & ONLRET) | |
364 | column = 0; | |
365 | break; | |
366 | default: | |
367 | column++; | |
368 | break; | |
1fd5e000 | 369 | } |
774ea162 CF |
370 | |
371 | /* Don't store data past the end of the user's buffer. This | |
372 | can happen if the user requests a read of 1 byte when | |
373 | doing \r\n expansion. */ | |
374 | if (optr - buf >= (int) len) | |
1fd5e000 | 375 | { |
774ea162 CF |
376 | if (*iptr != '\n' || n != 0) |
377 | system_printf ("internal error: %d unexpected characters", n); | |
3f0b4935 | 378 | need_nl = 1; |
774ea162 | 379 | break; |
1fd5e000 | 380 | } |
1fd5e000 | 381 | |
774ea162 | 382 | *optr++ = *iptr++; |
1fd5e000 | 383 | } |
774ea162 | 384 | } |
3f0b4935 | 385 | rc = optr - buf; |
774ea162 | 386 | break; |
1fd5e000 | 387 | |
774ea162 CF |
388 | err: |
389 | if (GetLastError () == ERROR_BROKEN_PIPE) | |
390 | rc = 0; | |
391 | else | |
392 | { | |
393 | __seterrno (); | |
394 | rc = -1; | |
1fd5e000 | 395 | } |
774ea162 | 396 | break; |
1fd5e000 | 397 | } |
774ea162 CF |
398 | |
399 | out: | |
400 | termios_printf ("returning %d", rc); | |
401 | return rc; | |
1fd5e000 CF |
402 | } |
403 | ||
404 | static DWORD WINAPI | |
c4ec64d7 | 405 | process_output (void *) |
1fd5e000 | 406 | { |
c4ec64d7 | 407 | char buf[OUT_BUFFER_SIZE * 2]; |
1fd5e000 | 408 | |
3f0b4935 | 409 | for (;;) |
1fd5e000 | 410 | { |
3f0b4935 | 411 | int n = tty_master->process_slave_output (buf, OUT_BUFFER_SIZE, 0); |
6b2a9a2f | 412 | if (n <= 0) |
1fd5e000 | 413 | { |
6b2a9a2f CF |
414 | if (n < 0) |
415 | termios_printf ("ReadFile %E"); | |
c4ec64d7 | 416 | ExitThread (0); |
1fd5e000 CF |
417 | } |
418 | n = tty_master->console->write ((void *) buf, (size_t) n); | |
29ac7f89 | 419 | tty_master->get_ttyp ()->write_error = n == -1 ? get_errno () : 0; |
1fd5e000 CF |
420 | } |
421 | } | |
422 | ||
423 | ||
424 | /* Process tty ioctl requests */ | |
425 | ||
426 | static DWORD WINAPI | |
9cec3d45 | 427 | process_ioctl (void *) |
1fd5e000 CF |
428 | { |
429 | while (1) | |
430 | { | |
431 | WaitForSingleObject (tty_master->ioctl_request_event, INFINITE); | |
432 | termios_printf ("ioctl() request"); | |
6258d96a CV |
433 | tty *ttyp = tty_master->get_ttyp (); |
434 | ttyp->ioctl_retval = | |
435 | tty_master->console->ioctl (ttyp->cmd, | |
436 | (ttyp->cmd == KDSKBMETA) | |
437 | ? (void *) ttyp->arg.value | |
438 | : (void *) &ttyp->arg); | |
1fd5e000 CF |
439 | SetEvent (tty_master->ioctl_done_event); |
440 | } | |
441 | } | |
442 | ||
443 | /**********************************************************************/ | |
444 | /* Tty slave stuff */ | |
445 | ||
0476bae5 | 446 | fhandler_tty_slave::fhandler_tty_slave () |
3378bdfc | 447 | : fhandler_tty_common (), inuse (NULL) |
1fd5e000 | 448 | { |
56551a9b | 449 | uninterruptible_io (true); |
1fd5e000 CF |
450 | } |
451 | ||
f3ea62a8 CF |
452 | /* FIXME: This function needs to close handles when it has |
453 | a failing condition. */ | |
1fd5e000 | 454 | int |
7ac61736 | 455 | fhandler_tty_slave::open (int flags, mode_t) |
1fd5e000 | 456 | { |
8e10c431 | 457 | if (get_device () == FH_TTY) |
c76ca047 | 458 | dev().tty_to_real_device (); |
5a0826c3 | 459 | fhandler_tty_slave *arch = (fhandler_tty_slave *) cygheap->fdtab.find_archetype (pc.dev); |
8e10c431 | 460 | if (arch) |
2e008fb9 | 461 | { |
8e10c431 | 462 | *this = *(fhandler_tty_slave *) arch; |
3378bdfc | 463 | termios_printf ("copied fhandler_tty_slave archetype"); |
0544d116 | 464 | set_flags ((flags & ~O_TEXT) | O_BINARY); |
59297e04 | 465 | cygheap->manage_console_count ("fhandler_tty_slave::open<arch>", 1); |
8e10c431 | 466 | goto out; |
2e008fb9 CF |
467 | } |
468 | ||
7ac61736 | 469 | tcinit (cygwin_shared->tty[get_unit ()]); |
1fd5e000 | 470 | |
71d59a92 | 471 | cygwin_shared->tty.attach (get_unit ()); |
1fd5e000 | 472 | |
1bc9effd | 473 | set_flags ((flags & ~O_TEXT) | O_BINARY); |
1fd5e000 | 474 | /* Create synchronisation events */ |
27114d3a | 475 | char buf[MAX_PATH]; |
1fd5e000 CF |
476 | |
477 | /* output_done_event may or may not exist. It will exist if the tty | |
478 | was opened by fhandler_tty_master::init, normally called at | |
479 | startup if use_tty is non-zero. It will not exist if this is a | |
480 | pty opened by fhandler_pty_master::open. In the former case, tty | |
481 | output is handled by a separate thread which controls output. */ | |
8bdfa78a | 482 | shared_name (buf, OUTPUT_DONE_EVENT, get_unit ()); |
1fd5e000 CF |
483 | output_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf); |
484 | ||
e62ac9e8 | 485 | if (!(output_mutex = get_ttyp ()->open_output_mutex ())) |
1fd5e000 CF |
486 | { |
487 | termios_printf ("open output mutex failed, %E"); | |
488 | __seterrno (); | |
489 | return 0; | |
490 | } | |
e62ac9e8 | 491 | if (!(input_mutex = get_ttyp ()->open_input_mutex ())) |
306c4b67 ED |
492 | { |
493 | termios_printf ("open input mutex failed, %E"); | |
494 | __seterrno (); | |
495 | return 0; | |
496 | } | |
8bdfa78a | 497 | shared_name (buf, INPUT_AVAILABLE_EVENT, get_unit ()); |
306c4b67 ED |
498 | if (!(input_available_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf))) |
499 | { | |
500 | termios_printf ("open input event failed, %E"); | |
501 | __seterrno (); | |
502 | return 0; | |
503 | } | |
1fd5e000 CF |
504 | |
505 | /* The ioctl events may or may not exist. See output_done_event, | |
506 | above. */ | |
8bdfa78a | 507 | shared_name (buf, IOCTL_REQUEST_EVENT, get_unit ()); |
1fd5e000 | 508 | ioctl_request_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf); |
8bdfa78a | 509 | shared_name (buf, IOCTL_DONE_EVENT, get_unit ()); |
1fd5e000 CF |
510 | ioctl_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf); |
511 | ||
512 | /* FIXME: Needs a method to eliminate tty races */ | |
513 | { | |
514 | acquire_output_mutex (500); | |
2496a363 | 515 | inuse = get_ttyp ()->create_inuse (TTY_SLAVE_ALIVE); |
2e008fb9 | 516 | get_ttyp ()->was_opened = true; |
1fd5e000 CF |
517 | release_output_mutex (); |
518 | } | |
519 | ||
3378bdfc | 520 | if (!get_ttyp ()->from_master || !get_ttyp ()->to_master) |
1fd5e000 CF |
521 | { |
522 | termios_printf ("tty handles have been closed"); | |
523 | set_errno (EACCES); | |
524 | return 0; | |
525 | } | |
526 | ||
8e10c431 CF |
527 | HANDLE from_master_local; |
528 | HANDLE to_master_local; | |
529 | from_master_local = to_master_local = NULL; | |
1fd5e000 | 530 | |
4392d36c | 531 | #ifdef USE_SERVER |
a76877e9 | 532 | if (cygserver_running == CYGSERVER_UNAVAIL |
3d8a75bd | 533 | || !cygserver_attach_tty (&from_master_local, &to_master_local)) |
3872e9a4 | 534 | #endif |
1fd5e000 | 535 | { |
71d59a92 CF |
536 | if (get_ttyp ()->master_pid < 0) |
537 | { | |
538 | set_errno (EAGAIN); | |
539 | termios_printf ("*** master is closed"); | |
540 | return 0; | |
541 | } | |
3378bdfc CF |
542 | pinfo p (get_ttyp ()->master_pid); |
543 | if (!p) | |
544 | { | |
545 | set_errno (EAGAIN); | |
546 | termios_printf ("*** couldn't find tty master"); | |
547 | return 0; | |
548 | } | |
e217832c | 549 | #ifdef USE_SERVER |
f449bfef | 550 | termios_printf ("cannot dup handles via server. using old method."); |
e217832c | 551 | #endif |
f449bfef | 552 | HANDLE tty_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE, |
3378bdfc | 553 | p->dwProcessId); |
f449bfef | 554 | if (tty_owner == NULL) |
2402700d CF |
555 | { |
556 | termios_printf ("can't open tty (%d) handle process %d", | |
7ac61736 | 557 | get_unit (), get_ttyp ()->master_pid); |
2402700d CF |
558 | __seterrno (); |
559 | return 0; | |
560 | } | |
561 | ||
562 | if (!DuplicateHandle (tty_owner, get_ttyp ()->from_master, | |
563 | hMainProc, &from_master_local, 0, TRUE, | |
f449bfef | 564 | DUPLICATE_SAME_ACCESS)) |
2402700d CF |
565 | { |
566 | termios_printf ("can't duplicate input, %E"); | |
567 | __seterrno (); | |
568 | return 0; | |
569 | } | |
f449bfef | 570 | |
f7239090 | 571 | VerifyHandle (from_master_local); |
2402700d | 572 | if (!DuplicateHandle (tty_owner, get_ttyp ()->to_master, |
f449bfef RC |
573 | hMainProc, &to_master_local, 0, TRUE, |
574 | DUPLICATE_SAME_ACCESS)) | |
2402700d CF |
575 | { |
576 | termios_printf ("can't duplicate output, %E"); | |
577 | __seterrno (); | |
578 | return 0; | |
579 | } | |
f7239090 | 580 | VerifyHandle (to_master_local); |
f449bfef | 581 | CloseHandle (tty_owner); |
1fd5e000 | 582 | } |
f449bfef RC |
583 | |
584 | termios_printf ("duplicated from_master %p->%p from tty_owner", | |
578e142a | 585 | get_ttyp ()->from_master, from_master_local); |
f449bfef | 586 | termios_printf ("duplicated to_master %p->%p from tty_owner", |
578e142a | 587 | get_ttyp ()->to_master, to_master_local); |
f449bfef RC |
588 | |
589 | set_io_handle (from_master_local); | |
f449bfef | 590 | set_output_handle (to_master_local); |
1fd5e000 | 591 | |
f3ea62a8 | 592 | set_open_status (); |
3b26b6a2 CF |
593 | if (cygheap->manage_console_count ("fhandler_tty_slave::open", 1) == 1 |
594 | && !output_done_event) | |
595 | fhandler_console::need_invisible (); | |
8e10c431 CF |
596 | |
597 | // FIXME: Do this better someday | |
ee4388c4 | 598 | arch = (fhandler_tty_slave *) cmalloc_abort (HEAP_ARCHETYPES, sizeof (*this)); |
8e10c431 CF |
599 | *((fhandler_tty_slave **) cygheap->fdtab.add_archetype ()) = arch; |
600 | archetype = arch; | |
601 | *arch = *this; | |
602 | ||
603 | out: | |
604 | usecount = 0; | |
3378bdfc | 605 | arch->usecount++; |
59297e04 | 606 | report_tty_counts (this, "opened", ""); |
8e10c431 | 607 | myself->set_ctty (get_ttyp (), flags, arch); |
1fd5e000 CF |
608 | |
609 | return 1; | |
610 | } | |
611 | ||
c418817e CF |
612 | int |
613 | fhandler_tty_slave::close () | |
614 | { | |
6118c524 CF |
615 | /* This used to always call fhandler_tty_common::close when hExeced but that |
616 | caused multiple closes of the handles associated with this tty. Since | |
617 | close_all_files is not called until after the cygwin process has synced | |
618 | or before a non-cygwin process has exited, it should be safe to just | |
619 | close this normally. cgf 2006-05-20 */ | |
620 | cygheap->manage_console_count ("fhandler_tty_slave::close", -1); | |
e9737793 | 621 | |
6118c524 CF |
622 | archetype->usecount--; |
623 | report_tty_counts (this, "closed", ""); | |
e9737793 | 624 | |
6118c524 CF |
625 | if (archetype->usecount) |
626 | { | |
9279515a | 627 | #ifdef DEBUGGING |
6118c524 CF |
628 | if (archetype->usecount < 0) |
629 | system_printf ("error: usecount %d", archetype->usecount); | |
9279515a | 630 | #endif |
6118c524 CF |
631 | termios_printf ("just returning because archetype usecount is != 0"); |
632 | return 0; | |
8e10c431 CF |
633 | } |
634 | ||
8ed5c9b6 | 635 | termios_printf ("closing last open %s handle", ttyname ()); |
3378bdfc CF |
636 | if (inuse && !CloseHandle (inuse)) |
637 | termios_printf ("CloseHandle (inuse), %E"); | |
9279515a | 638 | return fhandler_tty_common::close (); |
c418817e CF |
639 | } |
640 | ||
f449bfef RC |
641 | int |
642 | fhandler_tty_slave::cygserver_attach_tty (LPHANDLE from_master_ptr, | |
2402700d | 643 | LPHANDLE to_master_ptr) |
f449bfef | 644 | { |
4392d36c | 645 | #ifndef USE_SERVER |
3d8a75bd CF |
646 | return 0; |
647 | #else | |
f449bfef RC |
648 | if (!from_master_ptr || !to_master_ptr) |
649 | return 0; | |
650 | ||
1c001dd2 CS |
651 | client_request_attach_tty req ((DWORD) get_ttyp ()->master_pid, |
652 | (HANDLE) get_ttyp ()->from_master, | |
653 | (HANDLE) get_ttyp ()->to_master); | |
f449bfef | 654 | |
1c001dd2 | 655 | if (req.make_request () == -1 || req.error_code ()) |
f449bfef RC |
656 | return 0; |
657 | ||
1c001dd2 CS |
658 | *from_master_ptr = req.from_master (); |
659 | *to_master_ptr = req.to_master (); | |
3872e9a4 | 660 | |
f449bfef | 661 | return 1; |
3d8a75bd | 662 | #endif |
f449bfef RC |
663 | } |
664 | ||
1fd5e000 | 665 | void |
9cec3d45 | 666 | fhandler_tty_slave::init (HANDLE, DWORD a, mode_t) |
1fd5e000 | 667 | { |
7ac61736 | 668 | int flags = 0; |
1fd5e000 CF |
669 | |
670 | a &= GENERIC_READ | GENERIC_WRITE; | |
671 | if (a == GENERIC_READ) | |
7ac61736 | 672 | flags = O_RDONLY; |
1fd5e000 | 673 | if (a == GENERIC_WRITE) |
7ac61736 | 674 | flags = O_WRONLY; |
1fd5e000 | 675 | if (a == (GENERIC_READ | GENERIC_WRITE)) |
7ac61736 | 676 | flags = O_RDWR; |
1fd5e000 | 677 | |
7ac61736 | 678 | open (flags); |
1fd5e000 CF |
679 | } |
680 | ||
681 | int | |
682 | fhandler_tty_slave::write (const void *ptr, size_t len) | |
683 | { | |
684 | DWORD n, towrite = len; | |
685 | ||
7ac61736 | 686 | termios_printf ("tty%d, write(%x, %d)", get_unit (), ptr, len); |
1fd5e000 CF |
687 | |
688 | acquire_output_mutex (INFINITE); | |
689 | ||
690 | while (len) | |
691 | { | |
692 | n = min (OUT_BUFFER_SIZE, len); | |
693 | char *buf = (char *)ptr; | |
694 | ptr = (char *) ptr + n; | |
695 | len -= n; | |
696 | ||
29ac7f89 CF |
697 | /* Previous write may have set write_error to != 0. Check it here. |
698 | This is less than optimal, but the alternative slows down tty | |
699 | writes enormously. */ | |
700 | if (get_ttyp ()->write_error) | |
701 | { | |
702 | set_errno (get_ttyp ()->write_error); | |
703 | towrite = (DWORD) -1; | |
704 | break; | |
705 | } | |
706 | ||
1fd5e000 CF |
707 | if (WriteFile (get_output_handle (), buf, n, &n, NULL) == FALSE) |
708 | { | |
d824bcf9 | 709 | DWORD err = GetLastError (); |
1fd5e000 | 710 | termios_printf ("WriteFile failed, %E"); |
d824bcf9 CF |
711 | switch (err) |
712 | { | |
713 | case ERROR_NO_DATA: | |
714 | err = ERROR_IO_DEVICE; | |
715 | default: | |
a3ad8241 | 716 | __seterrno_from_win_error (err); |
d824bcf9 | 717 | } |
d25c187f | 718 | raise (SIGHUP); /* FIXME: Should this be SIGTTOU? */ |
d824bcf9 | 719 | towrite = (DWORD) -1; |
1fd5e000 CF |
720 | break; |
721 | } | |
722 | ||
723 | if (output_done_event != NULL) | |
724 | { | |
774ea162 CF |
725 | DWORD rc; |
726 | DWORD x = n * 1000; | |
727 | rc = WaitForSingleObject (output_done_event, x); | |
2116a175 | 728 | termios_printf ("waited %d ms for output_done_event, WFSO %d", x, rc); |
1fd5e000 | 729 | } |
1fd5e000 CF |
730 | } |
731 | release_output_mutex (); | |
732 | return towrite; | |
733 | } | |
734 | ||
8bce0d72 CF |
735 | void __stdcall |
736 | fhandler_tty_slave::read (void *ptr, size_t& len) | |
1fd5e000 | 737 | { |
1fd5e000 | 738 | int totalread = 0; |
207daf0f | 739 | int vmin = 0; |
1fd5e000 | 740 | int vtime = 0; /* Initialized to prevent -Wuninitialized warning */ |
306c4b67 ED |
741 | size_t readlen; |
742 | DWORD bytes_in_pipe; | |
1fd5e000 | 743 | char buf[INP_BUFFER_SIZE]; |
5e8e21d9 | 744 | char peek_buf[INP_BUFFER_SIZE]; |
306c4b67 ED |
745 | DWORD time_to_wait; |
746 | DWORD rc; | |
747 | HANDLE w4[2]; | |
1fd5e000 | 748 | |
f449bfef | 749 | termios_printf ("read(%x, %d) handle %p", ptr, len, get_handle ()); |
1fd5e000 | 750 | |
41946df6 CV |
751 | if (!ptr) /* Indicating tcflush(). */ |
752 | time_to_wait = 0; | |
753 | else if ((get_ttyp ()->ti.c_lflag & ICANON)) | |
207daf0f CF |
754 | time_to_wait = INFINITE; |
755 | else | |
1fd5e000 | 756 | { |
2701126a CF |
757 | vmin = get_ttyp ()->ti.c_cc[VMIN]; |
758 | if (vmin > INP_BUFFER_SIZE) | |
759 | vmin = INP_BUFFER_SIZE; | |
1fd5e000 | 760 | vtime = get_ttyp ()->ti.c_cc[VTIME]; |
7a1174dc CF |
761 | if (vmin < 0) |
762 | vmin = 0; | |
763 | if (vtime < 0) | |
764 | vtime = 0; | |
207daf0f CF |
765 | if (!vmin && !vtime) |
766 | time_to_wait = 0; | |
767 | else | |
768 | time_to_wait = !vtime ? INFINITE : 100 * vtime; | |
1fd5e000 | 769 | } |
306c4b67 ED |
770 | |
771 | w4[0] = signal_arrived; | |
772 | w4[1] = input_available_event; | |
1fd5e000 | 773 | |
b9cd8fb5 | 774 | DWORD waiter = time_to_wait; |
1fd5e000 CF |
775 | while (len) |
776 | { | |
207daf0f CF |
777 | rc = WaitForMultipleObjects (2, w4, FALSE, waiter); |
778 | ||
779 | if (rc == WAIT_TIMEOUT) | |
8cb359d9 CF |
780 | { |
781 | termios_printf ("wait timed out, waiter %u", waiter); | |
782 | break; | |
783 | } | |
207daf0f CF |
784 | |
785 | if (rc == WAIT_FAILED) | |
786 | { | |
787 | termios_printf ("wait for input event failed, %E"); | |
788 | break; | |
789 | } | |
790 | ||
306c4b67 | 791 | if (rc == WAIT_OBJECT_0) |
2116a175 | 792 | { |
ed4c976c | 793 | /* if we've received signal after successfully reading some data, |
2116a175 CF |
794 | just return all data successfully read */ |
795 | if (totalread > 0) | |
796 | break; | |
797 | set_sig_errno (EINTR); | |
6cce721b | 798 | len = (size_t) -1; |
8bce0d72 | 799 | return; |
2116a175 | 800 | } |
207daf0f | 801 | |
306c4b67 ED |
802 | rc = WaitForSingleObject (input_mutex, 1000); |
803 | if (rc == WAIT_FAILED) | |
2116a175 CF |
804 | { |
805 | termios_printf ("wait for input mutex failed, %E"); | |
806 | break; | |
807 | } | |
306c4b67 | 808 | else if (rc == WAIT_TIMEOUT) |
2116a175 CF |
809 | { |
810 | termios_printf ("failed to acquire input mutex after input event arrived"); | |
811 | break; | |
812 | } | |
c90e1cf1 | 813 | if (!PeekNamedPipe (get_handle (), peek_buf, sizeof (peek_buf), &bytes_in_pipe, NULL, NULL)) |
1fd5e000 | 814 | { |
306c4b67 | 815 | termios_printf ("PeekNamedPipe failed, %E"); |
d25c187f | 816 | raise (SIGHUP); |
2116a175 | 817 | bytes_in_pipe = 0; |
1fd5e000 | 818 | } |
207daf0f | 819 | |
41946df6 CV |
820 | /* On first peek determine no. of bytes to flush. */ |
821 | if (!ptr && len == UINT_MAX) | |
e3778517 | 822 | len = (size_t) bytes_in_pipe; |
41946df6 | 823 | |
b9b1b383 | 824 | if (ptr && !bytes_in_pipe && !vmin && !time_to_wait) |
207daf0f CF |
825 | { |
826 | ReleaseMutex (input_mutex); | |
bd893898 | 827 | len = (size_t) bytes_in_pipe; |
8bce0d72 | 828 | return; |
207daf0f CF |
829 | } |
830 | ||
306c4b67 | 831 | readlen = min (bytes_in_pipe, min (len, sizeof (buf))); |
207daf0f | 832 | |
41946df6 | 833 | if (ptr && vmin && readlen > (unsigned) vmin) |
207daf0f CF |
834 | readlen = vmin; |
835 | ||
836 | DWORD n = 0; | |
2116a175 CF |
837 | if (readlen) |
838 | { | |
839 | termios_printf ("reading %d bytes (vtime %d)", readlen, vtime); | |
840 | if (ReadFile (get_handle (), buf, readlen, &n, NULL) == FALSE) | |
841 | { | |
306c4b67 | 842 | termios_printf ("read failed, %E"); |
d25c187f | 843 | raise (SIGHUP); |
306c4b67 | 844 | } |
1ff9f4b9 CF |
845 | /* MSDN states that 5th prameter can be used to determine total |
846 | number of bytes in pipe, but for some reason this number doesn't | |
847 | change after successful read. So we have to peek into the pipe | |
848 | again to see if input is still available */ | |
849 | if (!PeekNamedPipe (get_handle (), peek_buf, 1, &bytes_in_pipe, NULL, NULL)) | |
5e8e21d9 ED |
850 | { |
851 | termios_printf ("PeekNamedPipe failed, %E"); | |
d25c187f | 852 | raise (SIGHUP); |
5e8e21d9 ED |
853 | bytes_in_pipe = 0; |
854 | } | |
2116a175 CF |
855 | if (n) |
856 | { | |
857 | len -= n; | |
858 | totalread += n; | |
41946df6 | 859 | if (ptr) |
e3778517 | 860 | { |
41946df6 CV |
861 | memcpy (ptr, buf, n); |
862 | ptr = (char *) ptr + n; | |
863 | } | |
2116a175 CF |
864 | } |
865 | } | |
306c4b67 | 866 | |
5e8e21d9 ED |
867 | if (!bytes_in_pipe) |
868 | ResetEvent (input_available_event); | |
306c4b67 ED |
869 | |
870 | ReleaseMutex (input_mutex); | |
3f0b4935 | 871 | |
41946df6 CV |
872 | if (!ptr) |
873 | { | |
874 | if (!bytes_in_pipe) | |
875 | break; | |
876 | continue; | |
e3778517 | 877 | } |
41946df6 | 878 | |
1fd5e000 CF |
879 | if (get_ttyp ()->read_retval < 0) // read error |
880 | { | |
881 | set_errno (-get_ttyp ()->read_retval); | |
882 | totalread = -1; | |
883 | break; | |
884 | } | |
885 | if (get_ttyp ()->read_retval == 0) //EOF | |
886 | { | |
887 | termios_printf ("saw EOF"); | |
888 | break; | |
889 | } | |
5fd12fb0 | 890 | if (get_ttyp ()->ti.c_lflag & ICANON || is_nonblocking ()) |
1fd5e000 | 891 | break; |
207daf0f | 892 | if (vmin && totalread >= vmin) |
2116a175 | 893 | break; |
306c4b67 | 894 | |
2116a175 | 895 | /* vmin == 0 && vtime == 0: |
306c4b67 ED |
896 | * we've already read all input, if any, so return immediately |
897 | * vmin == 0 && vtime > 0: | |
898 | * we've waited for input 10*vtime ms in WFSO(input_available_event), | |
899 | * no matter whether any input arrived, we shouldn't wait any longer, | |
2116a175 | 900 | * so return immediately |
306c4b67 ED |
901 | * vmin > 0 && vtime == 0: |
902 | * here, totalread < vmin, so continue waiting until more data | |
903 | * arrive | |
904 | * vmin > 0 && vtime > 0: | |
905 | * similar to the previous here, totalread < vmin, and timer | |
906 | * hadn't expired -- WFSO(input_available_event) != WAIT_TIMEOUT, | |
907 | * so "restart timer" and wait until more data arrive | |
908 | */ | |
909 | ||
2116a175 CF |
910 | if (vmin == 0) |
911 | break; | |
207daf0f CF |
912 | |
913 | if (n) | |
914 | waiter = time_to_wait; | |
1fd5e000 CF |
915 | } |
916 | termios_printf ("%d=read(%x, %d)", totalread, ptr, len); | |
bd893898 | 917 | len = (size_t) totalread; |
1fd5e000 CF |
918 | } |
919 | ||
c418817e | 920 | int |
dcb091ca | 921 | fhandler_tty_slave::dup (fhandler_base *child) |
c418817e | 922 | { |
8e10c431 CF |
923 | fhandler_tty_slave *arch = (fhandler_tty_slave *) archetype; |
924 | *(fhandler_tty_slave *) child = *arch; | |
4248960e | 925 | child->set_flags (get_flags ()); |
8e10c431 | 926 | child->usecount = 0; |
e9737793 | 927 | arch->usecount++; |
59297e04 CF |
928 | cygheap->manage_console_count ("fhandler_tty_slave::dup", 1); |
929 | report_tty_counts (child, "duped", ""); | |
8e10c431 | 930 | return 0; |
c418817e CF |
931 | } |
932 | ||
1fd5e000 | 933 | int |
3378bdfc | 934 | fhandler_pty_master::dup (fhandler_base *child) |
1fd5e000 | 935 | { |
3378bdfc CF |
936 | fhandler_tty_master *arch = (fhandler_tty_master *) archetype; |
937 | *(fhandler_tty_master *) child = *arch; | |
4248960e | 938 | child->set_flags (get_flags ()); |
3378bdfc CF |
939 | child->usecount = 0; |
940 | arch->usecount++; | |
941 | report_tty_counts (child, "duped master", ""); | |
1fd5e000 | 942 | return 0; |
1fd5e000 CF |
943 | } |
944 | ||
945 | int | |
946 | fhandler_tty_slave::tcgetattr (struct termios *t) | |
947 | { | |
948 | *t = get_ttyp ()->ti; | |
949 | return 0; | |
950 | } | |
951 | ||
952 | int | |
9cec3d45 | 953 | fhandler_tty_slave::tcsetattr (int, const struct termios *t) |
1fd5e000 CF |
954 | { |
955 | acquire_output_mutex (INFINITE); | |
956 | get_ttyp ()->ti = *t; | |
957 | release_output_mutex (); | |
958 | return 0; | |
959 | } | |
960 | ||
961 | int | |
41946df6 | 962 | fhandler_tty_slave::tcflush (int queue) |
1fd5e000 | 963 | { |
41946df6 CV |
964 | int ret = 0; |
965 | ||
966 | termios_printf ("tcflush(%d) handle %p", queue, get_handle ()); | |
967 | ||
968 | if (queue == TCIFLUSH || queue == TCIOFLUSH) | |
969 | { | |
970 | size_t len = UINT_MAX; | |
971 | read (NULL, len); | |
c8f07ce7 | 972 | ret = ((int) len) >= 0 ? 0 : -1; |
41946df6 CV |
973 | } |
974 | if (queue == TCOFLUSH || queue == TCIOFLUSH) | |
975 | { | |
976 | /* do nothing for now. */ | |
977 | } | |
978 | ||
979 | termios_printf ("%d=tcflush(%d)", ret, queue); | |
980 | return ret; | |
1fd5e000 CF |
981 | } |
982 | ||
1fd5e000 CF |
983 | int |
984 | fhandler_tty_slave::ioctl (unsigned int cmd, void *arg) | |
985 | { | |
986 | termios_printf ("ioctl (%x)", cmd); | |
987 | ||
7a1174dc | 988 | if (myself->pgid && get_ttyp ()->getpgid () != myself->pgid |
7ac61736 | 989 | && myself->ctty == get_unit () && (get_ttyp ()->ti.c_lflag & TOSTOP)) |
1fd5e000 CF |
990 | { |
991 | /* background process */ | |
59297e04 CF |
992 | termios_printf ("bg ioctl pgid %d, tpgid %d, %s", myself->pgid, |
993 | get_ttyp ()->getpgid (), myctty ()); | |
d25c187f | 994 | raise (SIGTTOU); |
1fd5e000 | 995 | } |
ad0bed74 | 996 | |
e64a50ed | 997 | int retval; |
1fd5e000 CF |
998 | switch (cmd) |
999 | { | |
1000 | case TIOCGWINSZ: | |
1fd5e000 | 1001 | case TIOCSWINSZ: |
63726a5e | 1002 | case TIOCLINUX: |
6258d96a CV |
1003 | case KDGKBMETA: |
1004 | case KDSKBMETA: | |
1fd5e000 CF |
1005 | break; |
1006 | case FIONBIO: | |
5fd12fb0 | 1007 | set_nonblocking (*(int *) arg); |
e64a50ed | 1008 | retval = 0; |
ad0bed74 | 1009 | goto out; |
1fd5e000 CF |
1010 | default: |
1011 | set_errno (EINVAL); | |
1012 | return -1; | |
1013 | } | |
ad0bed74 CF |
1014 | |
1015 | acquire_output_mutex (INFINITE); | |
1016 | ||
1017 | get_ttyp ()->cmd = cmd; | |
1018 | get_ttyp ()->ioctl_retval = 0; | |
f62412f2 | 1019 | int val; |
ad0bed74 CF |
1020 | switch (cmd) |
1021 | { | |
1022 | case TIOCGWINSZ: | |
1023 | get_ttyp ()->arg.winsize = get_ttyp ()->winsize; | |
1024 | if (ioctl_request_event) | |
1025 | SetEvent (ioctl_request_event); | |
2601ab57 | 1026 | *(struct winsize *) arg = get_ttyp ()->arg.winsize; |
ad0bed74 CF |
1027 | if (ioctl_done_event) |
1028 | WaitForSingleObject (ioctl_done_event, INFINITE); | |
1029 | get_ttyp ()->winsize = get_ttyp ()->arg.winsize; | |
1030 | break; | |
1031 | case TIOCSWINSZ: | |
2601ab57 CF |
1032 | if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row |
1033 | || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) | |
1034 | { | |
bcb4223c CF |
1035 | get_ttyp ()->arg.winsize = *(struct winsize *) arg; |
1036 | if (ioctl_request_event) | |
2601ab57 | 1037 | { |
bcb4223c | 1038 | get_ttyp ()->ioctl_retval = -EINVAL; |
a3cbb4a7 | 1039 | SetEvent (ioctl_request_event); |
bcb4223c CF |
1040 | } |
1041 | else | |
1042 | { | |
2601ab57 | 1043 | get_ttyp ()->winsize = *(struct winsize *) arg; |
f6936c48 | 1044 | killsys (-get_ttyp ()->getpgid (), SIGWINCH); |
2601ab57 | 1045 | } |
bcb4223c CF |
1046 | if (ioctl_done_event) |
1047 | WaitForSingleObject (ioctl_done_event, INFINITE); | |
2601ab57 | 1048 | } |
ad0bed74 | 1049 | break; |
63726a5e | 1050 | case TIOCLINUX: |
f62412f2 | 1051 | val = *(unsigned char *) arg; |
a3cbb4a7 CF |
1052 | if (val != 6 || !ioctl_request_event || !ioctl_done_event) |
1053 | get_ttyp ()->ioctl_retval = -EINVAL; | |
1054 | else | |
63726a5e | 1055 | { |
a3cbb4a7 | 1056 | get_ttyp ()->arg.value = val; |
63726a5e CF |
1057 | SetEvent (ioctl_request_event); |
1058 | WaitForSingleObject (ioctl_done_event, INFINITE); | |
a3cbb4a7 | 1059 | *(unsigned char *) arg = get_ttyp ()->arg.value & 0xFF; |
63726a5e CF |
1060 | } |
1061 | break; | |
6258d96a CV |
1062 | case KDGKBMETA: |
1063 | if (ioctl_request_event) | |
1064 | { | |
1065 | SetEvent (ioctl_request_event); | |
1066 | if (ioctl_done_event) | |
1067 | WaitForSingleObject (ioctl_done_event, INFINITE); | |
1068 | *(int *) arg = get_ttyp ()->arg.value; | |
1069 | } | |
1070 | else | |
1071 | get_ttyp ()->ioctl_retval = -EINVAL; | |
1072 | break; | |
1073 | case KDSKBMETA: | |
1074 | if (ioctl_request_event) | |
1075 | { | |
1076 | get_ttyp ()->arg.value = (int) arg; | |
1077 | SetEvent (ioctl_request_event); | |
1078 | if (ioctl_done_event) | |
1079 | WaitForSingleObject (ioctl_done_event, INFINITE); | |
1080 | } | |
1081 | else | |
1082 | get_ttyp ()->ioctl_retval = -EINVAL; | |
1083 | break; | |
ad0bed74 CF |
1084 | } |
1085 | ||
1086 | release_output_mutex (); | |
e64a50ed | 1087 | retval = get_ttyp ()->ioctl_retval; |
a3cbb4a7 CF |
1088 | if (retval < 0) |
1089 | { | |
1090 | set_errno (-retval); | |
1091 | retval = -1; | |
1092 | } | |
e64a50ed CF |
1093 | |
1094 | out: | |
a3cbb4a7 CF |
1095 | termios_printf ("%d = ioctl (%x)", retval, cmd); |
1096 | return retval; | |
1fd5e000 CF |
1097 | } |
1098 | ||
1099 | /******************************************************* | |
1100 | fhandler_pty_master | |
1101 | */ | |
7ac61736 | 1102 | fhandler_pty_master::fhandler_pty_master () |
3378bdfc | 1103 | : fhandler_tty_common (), pktmode (0), need_nl (0), dwProcessId (0) |
1fd5e000 | 1104 | { |
1fd5e000 CF |
1105 | } |
1106 | ||
1107 | int | |
7ac61736 | 1108 | fhandler_pty_master::open (int flags, mode_t) |
1fd5e000 | 1109 | { |
71d59a92 CF |
1110 | int ntty; |
1111 | ntty = cygwin_shared->tty.allocate (false); | |
7ac61736 | 1112 | if (ntty < 0) |
1fd5e000 CF |
1113 | return 0; |
1114 | ||
c76ca047 CF |
1115 | dev().devn = FHDEV (DEV_TTYM_MAJOR, ntty); |
1116 | if (!setup (true)) | |
3378bdfc | 1117 | { |
71d59a92 | 1118 | lock_ttys::release (); |
3378bdfc CF |
1119 | return 0; |
1120 | } | |
71d59a92 | 1121 | lock_ttys::release (); |
1bc9effd | 1122 | set_flags ((flags & ~O_TEXT) | O_BINARY); |
f3ea62a8 | 1123 | set_open_status (); |
3378bdfc CF |
1124 | // |
1125 | // FIXME: Do this better someday | |
ee4388c4 | 1126 | fhandler_pty_master *arch = (fhandler_tty_master *) cmalloc_abort (HEAP_ARCHETYPES, sizeof (*this)); |
3378bdfc CF |
1127 | *((fhandler_pty_master **) cygheap->fdtab.add_archetype ()) = arch; |
1128 | archetype = arch; | |
1129 | *arch = *this; | |
1130 | arch->dwProcessId = GetCurrentProcessId (); | |
1fd5e000 | 1131 | |
3378bdfc CF |
1132 | usecount = 0; |
1133 | arch->usecount++; | |
71d59a92 CF |
1134 | char buf[sizeof ("opened pty master for ttyNNNNNNNNNNN")]; |
1135 | __small_sprintf (buf, "opened pty master for tty%d", get_unit ()); | |
1136 | report_tty_counts (this, buf, ""); | |
1fd5e000 CF |
1137 | return 1; |
1138 | } | |
1139 | ||
4f3e6ff1 CF |
1140 | _off64_t |
1141 | fhandler_tty_common::lseek (_off64_t, int) | |
1142 | { | |
1143 | set_errno (ESPIPE); | |
1144 | return -1; | |
1145 | } | |
1146 | ||
1fd5e000 CF |
1147 | int |
1148 | fhandler_tty_common::close () | |
1149 | { | |
ce40c6ba | 1150 | termios_printf ("tty%d <%p,%p> closing", get_unit (), get_handle (), get_output_handle ()); |
1fd5e000 CF |
1151 | if (output_done_event && !CloseHandle (output_done_event)) |
1152 | termios_printf ("CloseHandle (output_done_event), %E"); | |
1153 | if (ioctl_done_event && !CloseHandle (ioctl_done_event)) | |
1154 | termios_printf ("CloseHandle (ioctl_done_event), %E"); | |
1155 | if (ioctl_request_event && !CloseHandle (ioctl_request_event)) | |
1156 | termios_printf ("CloseHandle (ioctl_request_event), %E"); | |
306c4b67 ED |
1157 | if (!ForceCloseHandle (input_mutex)) |
1158 | termios_printf ("CloseHandle (input_mutex<%p>), %E", input_mutex); | |
083abe54 CF |
1159 | if (!ForceCloseHandle (output_mutex)) |
1160 | termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex); | |
0d339267 CF |
1161 | if (!ForceCloseHandle1 (get_handle (), from_pty)) |
1162 | termios_printf ("CloseHandle (get_handle ()<%p>), %E", get_handle ()); | |
1163 | if (!ForceCloseHandle1 (get_output_handle (), to_pty)) | |
1164 | termios_printf ("CloseHandle (get_output_handle ()<%p>), %E", get_output_handle ()); | |
78ace8a7 | 1165 | |
306c4b67 ED |
1166 | if (!ForceCloseHandle (input_available_event)) |
1167 | termios_printf ("CloseHandle (input_available_event<%p>), %E", input_available_event); | |
1fd5e000 | 1168 | |
1fd5e000 CF |
1169 | return 0; |
1170 | } | |
1171 | ||
1172 | int | |
1173 | fhandler_pty_master::close () | |
1174 | { | |
1175 | #if 0 | |
1176 | while (accept_input () > 0) | |
1177 | continue; | |
1178 | #endif | |
3378bdfc CF |
1179 | archetype->usecount--; |
1180 | report_tty_counts (this, "closing master", ""); | |
1fd5e000 | 1181 | |
3378bdfc | 1182 | if (archetype->usecount) |
1fd5e000 | 1183 | { |
3378bdfc CF |
1184 | #ifdef DEBUGGING |
1185 | if (archetype->usecount < 0) | |
1186 | system_printf ("error: usecount %d", archetype->usecount); | |
92e19690 | 1187 | #endif |
3378bdfc CF |
1188 | termios_printf ("just returning because archetype usecount is != 0"); |
1189 | return 0; | |
1190 | } | |
0d339267 | 1191 | |
3378bdfc | 1192 | fhandler_tty_master *arch = (fhandler_tty_master *) archetype; |
313b51e6 CF |
1193 | termios_printf ("closing from_master(%p)/to_master(%p) since we own them(%d)", |
1194 | arch->from_master, arch->to_master, arch->dwProcessId); | |
1195 | if (!ForceCloseHandle (arch->from_master)) | |
1196 | termios_printf ("error closing from_master %p, %E", arch->from_master); | |
1197 | if (!ForceCloseHandle (arch->to_master)) | |
1198 | termios_printf ("error closing from_master %p, %E", arch->to_master); | |
3378bdfc | 1199 | fhandler_tty_common::close (); |
0d339267 | 1200 | |
71d59a92 CF |
1201 | if (hExeced || get_ttyp ()->master_pid != myself->pid) |
1202 | termios_printf ("not clearing: %d, master_pid %d", hExeced, get_ttyp ()->master_pid); | |
1203 | else | |
1204 | get_ttyp ()->set_master_closed (); | |
1fd5e000 CF |
1205 | |
1206 | return 0; | |
1207 | } | |
1208 | ||
1209 | int | |
1210 | fhandler_pty_master::write (const void *ptr, size_t len) | |
1211 | { | |
388aa994 | 1212 | int i; |
49dd6fc6 | 1213 | char *p = (char *) ptr; |
46b73ef1 CF |
1214 | termios ti = tc->ti; |
1215 | ||
1216 | for (i = 0; i < (int) len; i++) | |
388aa994 | 1217 | { |
46b73ef1 | 1218 | line_edit_status status = line_edit (p++, 1, ti); |
b18962e2 CF |
1219 | if (status > line_edit_signalled) |
1220 | { | |
1221 | if (status != line_edit_pipe_full) | |
1222 | i = -1; | |
1223 | break; | |
1224 | } | |
388aa994 | 1225 | } |
49dd6fc6 | 1226 | return i; |
1fd5e000 CF |
1227 | } |
1228 | ||
8bce0d72 CF |
1229 | void __stdcall |
1230 | fhandler_pty_master::read (void *ptr, size_t& len) | |
1fd5e000 | 1231 | { |
bd893898 | 1232 | len = (size_t) process_slave_output ((char *) ptr, len, pktmode); |
1fd5e000 CF |
1233 | } |
1234 | ||
1235 | int | |
1236 | fhandler_pty_master::tcgetattr (struct termios *t) | |
1237 | { | |
7ac61736 | 1238 | *t = cygwin_shared->tty[get_unit ()]->ti; |
1fd5e000 CF |
1239 | return 0; |
1240 | } | |
1241 | ||
1242 | int | |
9cec3d45 | 1243 | fhandler_pty_master::tcsetattr (int, const struct termios *t) |
1fd5e000 | 1244 | { |
7ac61736 | 1245 | cygwin_shared->tty[get_unit ()]->ti = *t; |
1fd5e000 CF |
1246 | return 0; |
1247 | } | |
1248 | ||
1249 | int | |
41946df6 | 1250 | fhandler_pty_master::tcflush (int queue) |
1fd5e000 | 1251 | { |
41946df6 CV |
1252 | int ret = 0; |
1253 | ||
1254 | termios_printf ("tcflush(%d) handle %p", queue, get_handle ()); | |
1255 | ||
1256 | if (queue == TCIFLUSH || queue == TCIOFLUSH) | |
1257 | ret = process_slave_output (NULL, OUT_BUFFER_SIZE, 0); | |
1258 | else if (queue == TCIFLUSH || queue == TCIOFLUSH) | |
1259 | { | |
1260 | /* do nothing for now. */ | |
1261 | } | |
1262 | ||
1263 | termios_printf ("%d=tcflush(%d)", ret, queue); | |
1264 | return ret; | |
1fd5e000 CF |
1265 | } |
1266 | ||
1267 | int | |
1268 | fhandler_pty_master::ioctl (unsigned int cmd, void *arg) | |
1269 | { | |
1270 | switch (cmd) | |
1271 | { | |
1272 | case TIOCPKT: | |
a3cbb4a7 | 1273 | pktmode = *(int *) arg; |
1fd5e000 CF |
1274 | break; |
1275 | case TIOCGWINSZ: | |
66dcfc44 | 1276 | *(struct winsize *) arg = get_ttyp ()->winsize; |
1fd5e000 CF |
1277 | break; |
1278 | case TIOCSWINSZ: | |
2601ab57 CF |
1279 | if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row |
1280 | || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) | |
1281 | { | |
a3cbb4a7 | 1282 | get_ttyp ()->winsize = *(struct winsize *) arg; |
f6936c48 | 1283 | killsys (-get_ttyp ()->getpgid (), SIGWINCH); |
2601ab57 | 1284 | } |
1fd5e000 CF |
1285 | break; |
1286 | case FIONBIO: | |
5fd12fb0 | 1287 | set_nonblocking (*(int *) arg); |
1fd5e000 CF |
1288 | break; |
1289 | default: | |
1290 | set_errno (EINVAL); | |
1291 | return -1; | |
1292 | } | |
1293 | return 0; | |
1294 | } | |
1295 | ||
1296 | char * | |
7ac61736 | 1297 | fhandler_pty_master::ptsname () |
1fd5e000 | 1298 | { |
d8a5f046 | 1299 | static char buf[TTY_NAME_MAX]; |
1fd5e000 | 1300 | |
7ac61736 | 1301 | __small_sprintf (buf, "/dev/tty%d", get_unit ()); |
1fd5e000 CF |
1302 | return buf; |
1303 | } | |
1304 | ||
1305 | void | |
56551a9b | 1306 | fhandler_tty_common::set_close_on_exec (bool val) |
1fd5e000 | 1307 | { |
3378bdfc CF |
1308 | // Cygwin processes will handle this specially on exec. |
1309 | close_on_exec (val); | |
1fd5e000 CF |
1310 | } |
1311 | ||
c418817e CF |
1312 | void |
1313 | fhandler_tty_slave::fixup_after_fork (HANDLE parent) | |
1314 | { | |
3378bdfc | 1315 | // fork_fixup (parent, inuse, "inuse"); |
8ed5c9b6 | 1316 | // fhandler_tty_common::fixup_after_fork (parent); |
59297e04 | 1317 | report_tty_counts (this, "inherited", ""); |
c418817e CF |
1318 | } |
1319 | ||
1fd5e000 | 1320 | void |
3378bdfc | 1321 | fhandler_tty_slave::fixup_after_exec () |
1fd5e000 | 1322 | { |
578e142a | 1323 | if (!close_on_exec ()) |
3378bdfc | 1324 | fixup_after_fork (NULL); |
1fd5e000 CF |
1325 | } |
1326 | ||
1fd5e000 CF |
1327 | int |
1328 | fhandler_tty_master::init_console () | |
1329 | { | |
7ac61736 | 1330 | console = (fhandler_console *) build_fh_dev (*console_dev, "/dev/ttym"); |
1fd5e000 CF |
1331 | if (console == NULL) |
1332 | return -1; | |
1333 | ||
1334 | console->init (INVALID_HANDLE_VALUE, GENERIC_READ | GENERIC_WRITE, O_BINARY); | |
59297e04 | 1335 | cygheap->manage_console_count ("fhandler_tty_master::init_console", -1, true); |
56551a9b | 1336 | console->uninterruptible_io (true); |
1fd5e000 CF |
1337 | return 0; |
1338 | } | |
3378bdfc CF |
1339 | |
1340 | #define close_maybe(h) \ | |
1341 | do { \ | |
1342 | if (h) \ | |
1343 | CloseHandle (h); \ | |
1344 | } while (0) | |
1345 | ||
1346 | bool | |
c76ca047 | 1347 | fhandler_pty_master::setup (bool ispty) |
3378bdfc | 1348 | { |
1ffe3e67 | 1349 | int res; |
c76ca047 CF |
1350 | tty& t = *cygwin_shared->tty[get_unit ()]; |
1351 | ||
3378bdfc CF |
1352 | tcinit (&t, true); /* Set termios information. Force initialization. */ |
1353 | ||
1354 | const char *errstr = NULL; | |
1355 | DWORD pipe_mode = PIPE_NOWAIT; | |
1356 | ||
1357 | /* Create communication pipes */ | |
1358 | ||
3378bdfc | 1359 | /* FIXME: should this be sec_none_nih? */ |
1ffe3e67 CF |
1360 | |
1361 | /* Create communication pipes */ | |
1362 | ||
1363 | char pipename[sizeof("ttyNNNN-from-master")]; | |
1364 | __small_sprintf (pipename, "tty%d-from-master", get_unit ()); | |
1365 | res = fhandler_pipe::create_selectable (&sec_all_nih, from_master, | |
1366 | get_output_handle (), 128 * 1024, | |
1367 | pipename); | |
1368 | if (res) | |
3378bdfc CF |
1369 | { |
1370 | errstr = "input pipe"; | |
1371 | goto err; | |
1372 | } | |
1373 | ||
1ffe3e67 CF |
1374 | __small_sprintf (pipename, "tty%d-to-master", get_unit ()); |
1375 | res = fhandler_pipe::create_selectable (&sec_all_nih, get_io_handle (), | |
1376 | to_master, 128 * 1024, pipename); | |
1377 | if (res) | |
3378bdfc CF |
1378 | { |
1379 | errstr = "output pipe"; | |
1380 | goto err; | |
1381 | } | |
1382 | ||
71d59a92 | 1383 | if (!SetNamedPipeHandleState (get_output_handle (), &pipe_mode, NULL, NULL)) |
3378bdfc CF |
1384 | termios_printf ("can't set output_handle(%p) to non-blocking mode", |
1385 | get_output_handle ()); | |
1386 | ||
1387 | need_nl = 0; | |
1388 | ||
1ffe3e67 | 1389 | /* We do not allow others to open us (for handle duplication) |
3378bdfc CF |
1390 | but rely on cygheap->inherited_ctty for descendant processes. |
1391 | In the future the cygserver may allow access by others. */ | |
1392 | ||
1393 | #ifdef USE_SERVER | |
a76877e9 CV |
1394 | if (cygserver_running == CYGSERVER_UNKNOWN) |
1395 | cygserver_init (); | |
3378bdfc CF |
1396 | #endif |
1397 | ||
1398 | /* Create synchronisation events */ | |
1399 | ||
c76ca047 | 1400 | if (!ispty) |
3378bdfc CF |
1401 | { |
1402 | if (!(output_done_event = t.get_event (errstr = OUTPUT_DONE_EVENT))) | |
1403 | goto err; | |
1404 | if (!(ioctl_done_event = t.get_event (errstr = IOCTL_DONE_EVENT))) | |
1405 | goto err; | |
1406 | if (!(ioctl_request_event = t.get_event (errstr = IOCTL_REQUEST_EVENT))) | |
1407 | goto err; | |
1408 | } | |
1409 | ||
1410 | if (!(input_available_event = t.get_event (errstr = INPUT_AVAILABLE_EVENT, TRUE))) | |
1411 | goto err; | |
1412 | ||
27114d3a | 1413 | char buf[MAX_PATH]; |
3378bdfc CF |
1414 | errstr = shared_name (buf, OUTPUT_MUTEX, t.ntty); |
1415 | if (!(output_mutex = CreateMutex (&sec_all, FALSE, buf))) | |
1416 | goto err; | |
1417 | ||
1418 | errstr = shared_name (buf, INPUT_MUTEX, t.ntty); | |
1419 | if (!(input_mutex = CreateMutex (&sec_all, FALSE, buf))) | |
1420 | goto err; | |
1421 | ||
1422 | if (!DuplicateHandle (hMainProc, from_master, hMainProc, &from_master, 0, false, | |
daff1587 | 1423 | DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) |
3378bdfc CF |
1424 | { |
1425 | errstr = "non-inheritable from_master"; | |
1426 | goto err; | |
1427 | } | |
1428 | ||
1429 | if (!DuplicateHandle (hMainProc, to_master, hMainProc, &to_master, 0, false, | |
daff1587 | 1430 | DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) |
3378bdfc CF |
1431 | { |
1432 | errstr = "non-inheritable to_master"; | |
1433 | goto err; | |
1434 | } | |
1435 | ||
1436 | t.from_master = from_master; | |
1437 | t.to_master = to_master; | |
1438 | // /* screws up tty master */ ProtectHandle1INH (output_mutex, output_mutex); | |
1439 | // /* screws up tty master */ ProtectHandle1INH (input_mutex, input_mutex); | |
1440 | t.winsize.ws_col = 80; | |
1441 | t.winsize.ws_row = 25; | |
1442 | t.master_pid = myself->pid; | |
1443 | ||
1444 | termios_printf ("tty%d opened - from_slave %p, to_slave %p", t.ntty, | |
1445 | get_io_handle (), get_output_handle ()); | |
1446 | return true; | |
1447 | ||
1448 | err: | |
1449 | __seterrno (); | |
1450 | close_maybe (get_io_handle ()); | |
1451 | close_maybe (get_output_handle ()); | |
1452 | close_maybe (output_done_event); | |
1453 | close_maybe (ioctl_done_event); | |
1454 | close_maybe (ioctl_request_event); | |
1455 | close_maybe (input_available_event); | |
1456 | close_maybe (output_mutex); | |
1457 | close_maybe (input_mutex); | |
578e142a CF |
1458 | close_maybe (from_master); |
1459 | close_maybe (to_master); | |
3378bdfc CF |
1460 | termios_printf ("tty%d open failed - failed to create %s", errstr); |
1461 | return false; | |
1462 | } | |
1463 | ||
1464 | void | |
1465 | fhandler_pty_master::fixup_after_fork (HANDLE parent) | |
1466 | { | |
1467 | DWORD wpid = GetCurrentProcessId (); | |
1468 | fhandler_tty_master *arch = (fhandler_tty_master *) archetype; | |
1469 | if (arch->dwProcessId != wpid) | |
1470 | { | |
1471 | tty& t = *get_ttyp (); | |
1472 | if (!DuplicateHandle (parent, arch->from_master, hMainProc, | |
1473 | &arch->from_master, 0, false, DUPLICATE_SAME_ACCESS)) | |
1474 | system_printf ("couldn't duplicate from_parent(%p), %E", arch->from_master); | |
1475 | if (!DuplicateHandle (parent, arch->to_master, hMainProc, | |
1476 | &arch->to_master, 0, false, DUPLICATE_SAME_ACCESS)) | |
1477 | system_printf ("couldn't duplicate to_parent(%p), %E", arch->from_master); | |
1478 | if (myself->pid == t.master_pid) | |
1479 | { | |
1480 | t.from_master = arch->from_master; | |
1481 | t.to_master = arch->to_master; | |
1482 | } | |
1483 | arch->dwProcessId = wpid; | |
1484 | } | |
daff1587 CF |
1485 | from_master = arch->from_master; |
1486 | to_master = arch->to_master; | |
3378bdfc CF |
1487 | report_tty_counts (this, "inherited master", ""); |
1488 | } | |
1489 | ||
1490 | void | |
1491 | fhandler_pty_master::fixup_after_exec () | |
1492 | { | |
578e142a | 1493 | if (!close_on_exec ()) |
3378bdfc | 1494 | fixup_after_fork (spawn_info->parent); |
daff1587 CF |
1495 | else |
1496 | from_master = to_master = NULL; | |
3378bdfc | 1497 | } |