]>
Commit | Line | Data |
---|---|---|
1fd5e000 CF |
1 | /* fhandler_termios.cc |
2 | ||
05c98e07 | 3 | Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, |
580df3b6 | 4 | 2010, 2011, 2012 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> |
1fd5e000 | 14 | #include <ctype.h> |
9e2baf8d | 15 | #include "cygerrno.h" |
7ac61736 | 16 | #include "path.h" |
bccd5e0d | 17 | #include "fhandler.h" |
bccd5e0d CF |
18 | #include "sigproc.h" |
19 | #include "pinfo.h" | |
29ac7f89 | 20 | #include "tty.h" |
5b3e1f73 | 21 | #include "cygtls.h" |
55dcba98 CF |
22 | #include "dtable.h" |
23 | #include "cygheap.h" | |
24 | #include "child_info.h" | |
bd139e52 | 25 | #include "ntdll.h" |
1fd5e000 CF |
26 | |
27 | /* Common functions shared by tty/console */ | |
28 | ||
29 | void | |
32bf3082 | 30 | fhandler_termios::tcinit (bool is_pty_master) |
1fd5e000 CF |
31 | { |
32 | /* Initial termios values */ | |
33 | ||
32bf3082 | 34 | if (is_pty_master || !tc ()->initialized ()) |
1fd5e000 | 35 | { |
32bf3082 CF |
36 | tc ()->ti.c_iflag = BRKINT | ICRNL | IXON; |
37 | tc ()->ti.c_oflag = OPOST | ONLCR; | |
38 | tc ()->ti.c_cflag = B38400 | CS8 | CREAD; | |
39 | tc ()->ti.c_lflag = ISIG | ICANON | ECHO | IEXTEN; | |
1fd5e000 | 40 | |
32bf3082 CF |
41 | tc ()->ti.c_cc[VDISCARD] = CFLUSH; |
42 | tc ()->ti.c_cc[VEOL] = CEOL; | |
43 | tc ()->ti.c_cc[VEOL2] = CEOL2; | |
44 | tc ()->ti.c_cc[VEOF] = CEOF; | |
45 | tc ()->ti.c_cc[VERASE] = CERASE; | |
46 | tc ()->ti.c_cc[VINTR] = CINTR; | |
47 | tc ()->ti.c_cc[VKILL] = CKILL; | |
48 | tc ()->ti.c_cc[VLNEXT] = CLNEXT; | |
49 | tc ()->ti.c_cc[VMIN] = 1; | |
50 | tc ()->ti.c_cc[VQUIT] = CQUIT; | |
51 | tc ()->ti.c_cc[VREPRINT] = CRPRNT; | |
52 | tc ()->ti.c_cc[VSTART] = CSTART; | |
53 | tc ()->ti.c_cc[VSTOP] = CSTOP; | |
54 | tc ()->ti.c_cc[VSUSP] = CSUSP; | |
55 | tc ()->ti.c_cc[VSWTC] = CSWTCH; | |
56 | tc ()->ti.c_cc[VTIME] = 0; | |
57 | tc ()->ti.c_cc[VWERASE] = CWERASE; | |
1fd5e000 | 58 | |
32bf3082 CF |
59 | tc ()->ti.c_ispeed = tc ()->ti.c_ospeed = B38400; |
60 | tc ()->pgid = is_pty_master ? 0 : myself->pgid; | |
61 | tc ()->initialized (true); | |
1fd5e000 CF |
62 | } |
63 | } | |
64 | ||
65 | int | |
66 | fhandler_termios::tcsetpgrp (const pid_t pgid) | |
67 | { | |
a345dc55 | 68 | termios_printf ("%s, pgid %d, sid %d, tsid %d", tc ()->ttyname (), pgid, |
32bf3082 CF |
69 | myself->sid, tc ()->getsid ()); |
70 | if (myself->sid != tc ()->getsid ()) | |
1fd5e000 CF |
71 | { |
72 | set_errno (EPERM); | |
73 | return -1; | |
74 | } | |
5b3e1f73 CF |
75 | int res; |
76 | while (1) | |
77 | { | |
78 | res = bg_check (-SIGTTOU); | |
79 | ||
80 | switch (res) | |
81 | { | |
82 | case bg_ok: | |
32bf3082 | 83 | tc ()->setpgid (pgid); |
ca35d41c | 84 | if (tc ()->is_console && (strace.active () || !being_debugged ())) |
852908e8 | 85 | tc ()->kill_pgrp (__SIGSETPGRP); |
5b3e1f73 CF |
86 | res = 0; |
87 | break; | |
88 | case bg_signalled: | |
89 | if (_my_tls.call_signal_handler ()) | |
90 | continue; | |
91 | set_errno (EINTR); | |
92 | /* fall through intentionally */ | |
93 | default: | |
94 | res = -1; | |
95 | break; | |
96 | } | |
97 | break; | |
98 | } | |
99 | return res; | |
1fd5e000 CF |
100 | } |
101 | ||
102 | int | |
103 | fhandler_termios::tcgetpgrp () | |
104 | { | |
bd7c9459 | 105 | if (myself->ctty > 0 && myself->ctty == tc ()->ntty) |
32bf3082 | 106 | return tc ()->pgid; |
b4311a90 CV |
107 | set_errno (ENOTTY); |
108 | return -1; | |
109 | } | |
110 | ||
111 | int | |
112 | fhandler_pty_master::tcgetpgrp () | |
113 | { | |
32bf3082 | 114 | return tc ()->pgid; |
7123c8b1 CF |
115 | } |
116 | ||
083abe54 CF |
117 | void |
118 | tty_min::kill_pgrp (int sig) | |
119 | { | |
069e637c | 120 | bool killself = false; |
deb648cc | 121 | winpids pids ((DWORD) PID_MAP_RW); |
985d0e68 | 122 | siginfo_t si = {0}; |
f6936c48 CF |
123 | si.si_signo = sig; |
124 | si.si_code = SI_KERNEL; | |
083abe54 CF |
125 | for (unsigned i = 0; i < pids.npids; i++) |
126 | { | |
127 | _pinfo *p = pids[i]; | |
1a9a235a | 128 | if (!p->exists () || p->ctty != ntty || p->pgid != pgid) |
083abe54 CF |
129 | continue; |
130 | if (p == myself) | |
069e637c | 131 | killself = sig != __SIGSETPGRP; |
083abe54 | 132 | else |
0c55f6ed | 133 | sig_send (p, si); |
083abe54 CF |
134 | } |
135 | if (killself) | |
f6936c48 | 136 | sig_send (myself, si); |
083abe54 | 137 | } |
2402700d | 138 | |
32bf3082 CF |
139 | int |
140 | tty_min::is_orphaned_process_group (int pgid) | |
141 | { | |
142 | /* An orphaned process group is a process group in which the parent | |
143 | of every member is either itself a member of the group or is not | |
144 | a member of the group's session. */ | |
145 | termios_printf ("checking pgid %d, my sid %d, my parent %d", pgid, myself->sid, myself->ppid); | |
146 | winpids pids ((DWORD) 0); | |
147 | for (unsigned i = 0; i < pids.npids; i++) | |
148 | { | |
149 | _pinfo *p = pids[i]; | |
150 | termios_printf ("checking pid %d - has pgid %d\n", p->pid, p->pgid); | |
151 | if (!p || !p->exists () || p->pgid != pgid) | |
b86f999a | 152 | continue; |
32bf3082 CF |
153 | pinfo ppid (p->ppid); |
154 | if (!ppid) | |
155 | continue; | |
156 | termios_printf ("ppid->pgid %d, ppid->sid %d", ppid->pgid, ppid->sid); | |
157 | if (ppid->pgid != pgid && ppid->sid == myself->sid) | |
b86f999a | 158 | return 0; |
32bf3082 CF |
159 | } |
160 | return 1; | |
161 | } | |
162 | ||
a7cde2b9 | 163 | bg_check_types |
9cec3d45 | 164 | fhandler_termios::bg_check (int sig) |
1fd5e000 | 165 | { |
32bf3082 CF |
166 | if (!myself->pgid || tc ()->getpgid () == myself->pgid || |
167 | myself->ctty != tc ()->ntty || | |
168 | ((sig == SIGTTOU) && !(tc ()->ti.c_lflag & TOSTOP))) | |
a7cde2b9 | 169 | return bg_ok; |
1fd5e000 CF |
170 | |
171 | if (sig < 0) | |
172 | sig = -sig; | |
173 | ||
a345dc55 CF |
174 | termios_printf ("%s, bg I/O pgid %d, tpgid %d, myctty %s", tc ()->ttyname (), |
175 | myself->pgid, tc ()->getpgid (), myctty ()); | |
1fd5e000 | 176 | |
32bf3082 | 177 | if (tc ()->getsid () == 0) |
1fd5e000 CF |
178 | { |
179 | /* The pty has been closed by the master. Return an EOF | |
180 | indication. FIXME: There is nothing to stop somebody | |
181 | from reallocating this pty. I think this is the case | |
182 | which is handled by unlockpt on a Unix system. */ | |
183 | termios_printf ("closed by master"); | |
a7cde2b9 | 184 | return bg_eof; |
1fd5e000 CF |
185 | } |
186 | ||
1fd5e000 | 187 | int sigs_ignored = |
13584f07 | 188 | ((void *) global_sigs[sig].sa_handler == (void *) SIG_IGN) || |
70300fdb | 189 | (_main_tls->sigmask & SIGTOMASK (sig)); |
1fd5e000 | 190 | |
7123c8b1 CF |
191 | /* If the process is ignoring SIGTT*, then background IO is OK. If |
192 | the process is not ignoring SIGTT*, then the sig is to be sent to | |
193 | all processes in the process group (unless the process group of the | |
194 | process is orphaned, in which case we return EIO). */ | |
195 | if (sigs_ignored) | |
196 | return bg_ok; /* Just allow the IO */ | |
32bf3082 | 197 | else if (tc ()->is_orphaned_process_group (myself->pgid)) |
f6936c48 | 198 | { |
7123c8b1 CF |
199 | termios_printf ("process group is orphaned"); |
200 | set_errno (EIO); /* This is an IO error */ | |
201 | return bg_error; | |
f6936c48 | 202 | } |
7123c8b1 CF |
203 | else |
204 | { | |
205 | /* Don't raise a SIGTT* signal if we have already been | |
206 | interrupted by another signal. */ | |
962f9a2c | 207 | if (cygwait ((DWORD) 0) != WAIT_SIGNALED) |
7123c8b1 CF |
208 | { |
209 | siginfo_t si = {0}; | |
210 | si.si_signo = sig; | |
211 | si.si_code = SI_KERNEL; | |
212 | kill_pgrp (myself->pgid, si); | |
213 | } | |
214 | return bg_signalled; | |
b86f999a | 215 | } |
1fd5e000 CF |
216 | } |
217 | ||
218 | #define set_input_done(x) input_done = input_done || (x) | |
219 | ||
cd94b71c CF |
220 | inline void |
221 | fhandler_termios::echo_erase (int force) | |
222 | { | |
32bf3082 | 223 | if (force || tc ()->ti.c_lflag & ECHO) |
cd94b71c CF |
224 | doecho ("\b \b", 3); |
225 | } | |
226 | ||
49dd6fc6 | 227 | line_edit_status |
46b73ef1 | 228 | fhandler_termios::line_edit (const char *rptr, int nread, termios& ti) |
1fd5e000 | 229 | { |
49dd6fc6 | 230 | line_edit_status ret = line_edit_ok; |
1fd5e000 CF |
231 | char c; |
232 | int input_done = 0; | |
b18962e2 | 233 | bool sawsig = false; |
46b73ef1 | 234 | int iscanon = ti.c_lflag & ICANON; |
1fd5e000 CF |
235 | |
236 | while (nread-- > 0) | |
237 | { | |
238 | c = *rptr++; | |
239 | ||
240 | termios_printf ("char %c", c); | |
241 | ||
242 | /* Check for special chars */ | |
243 | ||
244 | if (c == '\r') | |
245 | { | |
46b73ef1 | 246 | if (ti.c_iflag & IGNCR) |
1fd5e000 | 247 | continue; |
46b73ef1 | 248 | if (ti.c_iflag & ICRNL) |
1fd5e000 CF |
249 | { |
250 | c = '\n'; | |
251 | set_input_done (iscanon); | |
252 | } | |
253 | } | |
254 | else if (c == '\n') | |
255 | { | |
46b73ef1 | 256 | if (ti.c_iflag & INLCR) |
1fd5e000 CF |
257 | c = '\r'; |
258 | else | |
259 | set_input_done (iscanon); | |
260 | } | |
261 | ||
46b73ef1 | 262 | if (ti.c_iflag & ISTRIP) |
1fd5e000 | 263 | c &= 0x7f; |
46b73ef1 | 264 | if (ti.c_lflag & ISIG) |
1fd5e000 CF |
265 | { |
266 | int sig; | |
46b73ef1 | 267 | if (CCEQ (ti.c_cc[VINTR], c)) |
1fd5e000 | 268 | sig = SIGINT; |
46b73ef1 | 269 | else if (CCEQ (ti.c_cc[VQUIT], c)) |
1fd5e000 | 270 | sig = SIGQUIT; |
46b73ef1 | 271 | else if (CCEQ (ti.c_cc[VSUSP], c)) |
1fd5e000 CF |
272 | sig = SIGTSTP; |
273 | else | |
274 | goto not_a_sig; | |
275 | ||
276 | termios_printf ("got interrupt %d, sending signal %d", c, sig); | |
5e8e21d9 | 277 | eat_readahead (-1); |
32bf3082 | 278 | tc ()->kill_pgrp (sig); |
46b73ef1 | 279 | ti.c_lflag &= ~FLUSHO; |
b18962e2 | 280 | sawsig = true; |
1fd5e000 CF |
281 | goto restart_output; |
282 | } | |
283 | not_a_sig: | |
46b73ef1 | 284 | if (ti.c_iflag & IXON) |
1fd5e000 | 285 | { |
46b73ef1 | 286 | if (CCEQ (ti.c_cc[VSTOP], c)) |
1fd5e000 | 287 | { |
32bf3082 | 288 | if (!tc ()->output_stopped) |
c1644acb | 289 | { |
32bf3082 | 290 | tc ()->output_stopped = 1; |
c1644acb CF |
291 | acquire_output_mutex (INFINITE); |
292 | } | |
1fd5e000 CF |
293 | continue; |
294 | } | |
46b73ef1 | 295 | else if (CCEQ (ti.c_cc[VSTART], c)) |
1fd5e000 CF |
296 | { |
297 | restart_output: | |
32bf3082 | 298 | tc ()->output_stopped = 0; |
c1644acb | 299 | release_output_mutex (); |
1fd5e000 CF |
300 | continue; |
301 | } | |
32bf3082 | 302 | else if ((ti.c_iflag & IXANY) && tc ()->output_stopped) |
1fd5e000 CF |
303 | goto restart_output; |
304 | } | |
46b73ef1 | 305 | if (iscanon && ti.c_lflag & IEXTEN && CCEQ (ti.c_cc[VDISCARD], c)) |
1fd5e000 | 306 | { |
46b73ef1 | 307 | ti.c_lflag ^= FLUSHO; |
1fd5e000 CF |
308 | continue; |
309 | } | |
310 | if (!iscanon) | |
311 | /* nothing */; | |
46b73ef1 | 312 | else if (CCEQ (ti.c_cc[VERASE], c)) |
1fd5e000 CF |
313 | { |
314 | if (eat_readahead (1)) | |
cd94b71c | 315 | echo_erase (); |
1fd5e000 CF |
316 | continue; |
317 | } | |
46b73ef1 | 318 | else if (CCEQ (ti.c_cc[VWERASE], c)) |
1fd5e000 CF |
319 | { |
320 | int ch; | |
321 | do | |
322 | if (!eat_readahead (1)) | |
323 | break; | |
324 | else | |
cd94b71c | 325 | echo_erase (); |
1fd5e000 CF |
326 | while ((ch = peek_readahead (1)) >= 0 && !isspace (ch)); |
327 | continue; | |
328 | } | |
46b73ef1 | 329 | else if (CCEQ (ti.c_cc[VKILL], c)) |
1fd5e000 CF |
330 | { |
331 | int nchars = eat_readahead (-1); | |
46b73ef1 | 332 | if (ti.c_lflag & ECHO) |
cd94b71c CF |
333 | while (nchars--) |
334 | echo_erase (1); | |
1fd5e000 CF |
335 | continue; |
336 | } | |
46b73ef1 | 337 | else if (CCEQ (ti.c_cc[VREPRINT], c)) |
1fd5e000 | 338 | { |
46b73ef1 | 339 | if (ti.c_lflag & ECHO) |
cd94b71c CF |
340 | { |
341 | doecho ("\n\r", 2); | |
342 | doecho (rabuf, ralen); | |
343 | } | |
1fd5e000 CF |
344 | continue; |
345 | } | |
46b73ef1 | 346 | else if (CCEQ (ti.c_cc[VEOF], c)) |
1fd5e000 CF |
347 | { |
348 | termios_printf ("EOF"); | |
0c55f6ed | 349 | accept_input (); |
bd2001ae | 350 | ret = line_edit_input_done; |
1fd5e000 CF |
351 | continue; |
352 | } | |
46b73ef1 CF |
353 | else if (CCEQ (ti.c_cc[VEOL], c) || |
354 | CCEQ (ti.c_cc[VEOL2], c) || | |
1fd5e000 CF |
355 | c == '\n') |
356 | { | |
357 | set_input_done (1); | |
358 | termios_printf ("EOL"); | |
359 | } | |
360 | ||
46b73ef1 | 361 | if (ti.c_iflag & IUCLC && isupper (c)) |
2556e737 | 362 | c = cyg_tolower (c); |
1fd5e000 | 363 | |
49dd6fc6 | 364 | put_readahead (c); |
caf27c01 CF |
365 | if (ti.c_lflag & ECHO) |
366 | doecho (&c, 1); | |
46b73ef1 | 367 | if (!iscanon || input_done) |
bd2001ae | 368 | { |
388aa994 | 369 | int status = accept_input (); |
0c0eec3f | 370 | if (status != 1) |
b2be3149 | 371 | { |
388aa994 | 372 | ret = status ? line_edit_error : line_edit_pipe_full; |
b2be3149 CF |
373 | eat_readahead (1); |
374 | break; | |
375 | } | |
bd2001ae CF |
376 | ret = line_edit_input_done; |
377 | input_done = 0; | |
378 | } | |
1fd5e000 CF |
379 | } |
380 | ||
46b73ef1 | 381 | if (!iscanon && ralen > 0) |
bd2001ae | 382 | ret = line_edit_input_done; |
1fd5e000 | 383 | |
95a8465b | 384 | if (sawsig) |
49dd6fc6 | 385 | ret = line_edit_signalled; |
1fd5e000 | 386 | |
49dd6fc6 | 387 | return ret; |
1fd5e000 | 388 | } |
e935fcf0 | 389 | |
1727fba0 CV |
390 | _off64_t |
391 | fhandler_termios::lseek (_off64_t, int) | |
b0359971 RC |
392 | { |
393 | set_errno (ESPIPE); | |
2402700d | 394 | return -1; |
b0359971 | 395 | } |
f4c1f003 CF |
396 | |
397 | void | |
398 | fhandler_termios::sigflush () | |
399 | { | |
71c17c54 CF |
400 | /* FIXME: Checking get_ttyp() for NULL is not right since it should not |
401 | be NULL while this is alive. However, we can conceivably close a | |
402 | ctty while exiting and that will zero this. */ | |
55dcba98 CF |
403 | if ((!have_execed || have_execed_cygwin) && get_ttyp () |
404 | && !(get_ttyp ()->ti.c_lflag & NOFLSH)) | |
f4c1f003 CF |
405 | tcflush (TCIFLUSH); |
406 | } | |
4add6f8d | 407 | |
580df3b6 | 408 | pid_t |
4add6f8d CF |
409 | fhandler_termios::tcgetsid () |
410 | { | |
bd7c9459 | 411 | if (myself->ctty > 0 && myself->ctty == tc ()->ntty) |
4add6f8d CF |
412 | return tc ()->getsid (); |
413 | set_errno (ENOTTY); | |
414 | return -1; | |
415 | } | |
416 | ||
417 | int | |
23771fa1 | 418 | fhandler_termios::ioctl (int cmd, void *varg) |
4add6f8d CF |
419 | { |
420 | if (cmd != TIOCSCTTY) | |
421 | return 1; /* Not handled by this function */ | |
422 | ||
23771fa1 CF |
423 | int arg = (int) varg; |
424 | ||
4add6f8d CF |
425 | if (arg != 0 && arg != 1) |
426 | { | |
427 | set_errno (EINVAL); | |
428 | return -1; | |
429 | } | |
430 | ||
431 | termios_printf ("myself->ctty %d, myself->sid %d, myself->pid %d, arg %d, tc()->getsid () %d\n", | |
432 | myself->ctty, myself->sid, myself->pid, arg, tc ()->getsid ()); | |
433 | if (myself->ctty > 0 || myself->sid != myself->pid || (!arg && tc ()->getsid () > 0)) | |
434 | { | |
435 | set_errno (EPERM); | |
436 | return -1; | |
437 | } | |
438 | ||
439 | myself->ctty = -1; | |
23771fa1 | 440 | myself->set_ctty (this, 0); |
4add6f8d CF |
441 | return 0; |
442 | } |