]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygwin/fhandler_termios.cc
7fddba5b3fc045b76fc332a1b4946d244a480713
[newlib-cygwin.git] / winsup / cygwin / fhandler_termios.cc
1 /* fhandler_termios.cc
2
3 Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009,
4 2010, 2011, 2012 Red Hat, Inc.
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
12 #include "winsup.h"
13 #include <stdlib.h>
14 #include <ctype.h>
15 #include "cygerrno.h"
16 #include "path.h"
17 #include "fhandler.h"
18 #include "sigproc.h"
19 #include "pinfo.h"
20 #include "tty.h"
21 #include "cygtls.h"
22 #include "dtable.h"
23 #include "cygheap.h"
24 #include "child_info.h"
25 #include "ntdll.h"
26
27 /* Common functions shared by tty/console */
28
29 void
30 fhandler_termios::tcinit (bool is_pty_master)
31 {
32 /* Initial termios values */
33
34 if (is_pty_master || !tc ()->initialized ())
35 {
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;
40
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;
58
59 tc ()->ti.c_ispeed = tc ()->ti.c_ospeed = B38400;
60 tc ()->pgid = is_pty_master ? 0 : myself->pgid;
61 tc ()->initialized (true);
62 }
63 }
64
65 int
66 fhandler_termios::tcsetpgrp (const pid_t pgid)
67 {
68 termios_printf ("%s, pgid %d, sid %d, tsid %d", tc ()->ttyname (), pgid,
69 myself->sid, tc ()->getsid ());
70 if (myself->sid != tc ()->getsid ())
71 {
72 set_errno (EPERM);
73 return -1;
74 }
75 int res;
76 while (1)
77 {
78 res = bg_check (-SIGTTOU);
79
80 switch (res)
81 {
82 case bg_ok:
83 tc ()->setpgid (pgid);
84 if (tc ()->is_console && (strace.active () || !being_debugged ()))
85 tc ()->kill_pgrp (__SIGSETPGRP);
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;
100 }
101
102 int
103 fhandler_termios::tcgetpgrp ()
104 {
105 if (myself->ctty > 0 && myself->ctty == tc ()->ntty)
106 return tc ()->pgid;
107 set_errno (ENOTTY);
108 return -1;
109 }
110
111 int
112 fhandler_pty_master::tcgetpgrp ()
113 {
114 return tc ()->pgid;
115 }
116
117 void
118 tty_min::kill_pgrp (int sig)
119 {
120 bool killself = false;
121 winpids pids ((DWORD) PID_MAP_RW);
122 siginfo_t si = {0};
123 si.si_signo = sig;
124 si.si_code = SI_KERNEL;
125 for (unsigned i = 0; i < pids.npids; i++)
126 {
127 _pinfo *p = pids[i];
128 if (!p->exists () || p->ctty != ntty || p->pgid != pgid)
129 continue;
130 if (p == myself)
131 killself = sig != __SIGSETPGRP;
132 else
133 sig_send (p, si);
134 }
135 if (killself)
136 sig_send (myself, si);
137 }
138
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)
152 continue;
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)
158 return 0;
159 }
160 return 1;
161 }
162
163 bg_check_types
164 fhandler_termios::bg_check (int sig)
165 {
166 if (!myself->pgid || tc ()->getpgid () == myself->pgid ||
167 myself->ctty != tc ()->ntty ||
168 ((sig == SIGTTOU) && !(tc ()->ti.c_lflag & TOSTOP)))
169 return bg_ok;
170
171 if (sig < 0)
172 sig = -sig;
173
174 termios_printf ("%s, bg I/O pgid %d, tpgid %d, myctty %s", tc ()->ttyname (),
175 myself->pgid, tc ()->getpgid (), myctty ());
176
177 if (tc ()->getsid () == 0)
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");
184 return bg_eof;
185 }
186
187 int sigs_ignored =
188 ((void *) global_sigs[sig].sa_handler == (void *) SIG_IGN) ||
189 (_main_tls->sigmask & SIGTOMASK (sig));
190
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 */
197 else if (tc ()->is_orphaned_process_group (myself->pgid))
198 {
199 termios_printf ("process group is orphaned");
200 set_errno (EIO); /* This is an IO error */
201 return bg_error;
202 }
203 else
204 {
205 /* Don't raise a SIGTT* signal if we have already been
206 interrupted by another signal. */
207 if (WaitForSingleObject (signal_arrived, 0) != WAIT_OBJECT_0)
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;
215 }
216 }
217
218 #define set_input_done(x) input_done = input_done || (x)
219
220 inline void
221 fhandler_termios::echo_erase (int force)
222 {
223 if (force || tc ()->ti.c_lflag & ECHO)
224 doecho ("\b \b", 3);
225 }
226
227 line_edit_status
228 fhandler_termios::line_edit (const char *rptr, int nread, termios& ti)
229 {
230 line_edit_status ret = line_edit_ok;
231 char c;
232 int input_done = 0;
233 bool sawsig = false;
234 int iscanon = ti.c_lflag & ICANON;
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 {
246 if (ti.c_iflag & IGNCR)
247 continue;
248 if (ti.c_iflag & ICRNL)
249 {
250 c = '\n';
251 set_input_done (iscanon);
252 }
253 }
254 else if (c == '\n')
255 {
256 if (ti.c_iflag & INLCR)
257 c = '\r';
258 else
259 set_input_done (iscanon);
260 }
261
262 if (ti.c_iflag & ISTRIP)
263 c &= 0x7f;
264 if (ti.c_lflag & ISIG)
265 {
266 int sig;
267 if (CCEQ (ti.c_cc[VINTR], c))
268 sig = SIGINT;
269 else if (CCEQ (ti.c_cc[VQUIT], c))
270 sig = SIGQUIT;
271 else if (CCEQ (ti.c_cc[VSUSP], c))
272 sig = SIGTSTP;
273 else
274 goto not_a_sig;
275
276 termios_printf ("got interrupt %d, sending signal %d", c, sig);
277 eat_readahead (-1);
278 tc ()->kill_pgrp (sig);
279 ti.c_lflag &= ~FLUSHO;
280 sawsig = true;
281 goto restart_output;
282 }
283 not_a_sig:
284 if (ti.c_iflag & IXON)
285 {
286 if (CCEQ (ti.c_cc[VSTOP], c))
287 {
288 if (!tc ()->output_stopped)
289 {
290 tc ()->output_stopped = 1;
291 acquire_output_mutex (INFINITE);
292 }
293 continue;
294 }
295 else if (CCEQ (ti.c_cc[VSTART], c))
296 {
297 restart_output:
298 tc ()->output_stopped = 0;
299 release_output_mutex ();
300 continue;
301 }
302 else if ((ti.c_iflag & IXANY) && tc ()->output_stopped)
303 goto restart_output;
304 }
305 if (iscanon && ti.c_lflag & IEXTEN && CCEQ (ti.c_cc[VDISCARD], c))
306 {
307 ti.c_lflag ^= FLUSHO;
308 continue;
309 }
310 if (!iscanon)
311 /* nothing */;
312 else if (CCEQ (ti.c_cc[VERASE], c))
313 {
314 if (eat_readahead (1))
315 echo_erase ();
316 continue;
317 }
318 else if (CCEQ (ti.c_cc[VWERASE], c))
319 {
320 int ch;
321 do
322 if (!eat_readahead (1))
323 break;
324 else
325 echo_erase ();
326 while ((ch = peek_readahead (1)) >= 0 && !isspace (ch));
327 continue;
328 }
329 else if (CCEQ (ti.c_cc[VKILL], c))
330 {
331 int nchars = eat_readahead (-1);
332 if (ti.c_lflag & ECHO)
333 while (nchars--)
334 echo_erase (1);
335 continue;
336 }
337 else if (CCEQ (ti.c_cc[VREPRINT], c))
338 {
339 if (ti.c_lflag & ECHO)
340 {
341 doecho ("\n\r", 2);
342 doecho (rabuf, ralen);
343 }
344 continue;
345 }
346 else if (CCEQ (ti.c_cc[VEOF], c))
347 {
348 termios_printf ("EOF");
349 accept_input ();
350 ret = line_edit_input_done;
351 continue;
352 }
353 else if (CCEQ (ti.c_cc[VEOL], c) ||
354 CCEQ (ti.c_cc[VEOL2], c) ||
355 c == '\n')
356 {
357 set_input_done (1);
358 termios_printf ("EOL");
359 }
360
361 if (ti.c_iflag & IUCLC && isupper (c))
362 c = cyg_tolower (c);
363
364 put_readahead (c);
365 if (ti.c_lflag & ECHO)
366 doecho (&c, 1);
367 if (!iscanon || input_done)
368 {
369 int status = accept_input ();
370 if (status != 1)
371 {
372 ret = status ? line_edit_error : line_edit_pipe_full;
373 eat_readahead (1);
374 break;
375 }
376 ret = line_edit_input_done;
377 input_done = 0;
378 }
379 }
380
381 if (!iscanon && ralen > 0)
382 ret = line_edit_input_done;
383
384 if (sawsig)
385 ret = line_edit_signalled;
386
387 return ret;
388 }
389
390 _off64_t
391 fhandler_termios::lseek (_off64_t, int)
392 {
393 set_errno (ESPIPE);
394 return -1;
395 }
396
397 void
398 fhandler_termios::sigflush ()
399 {
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. */
403 if ((!have_execed || have_execed_cygwin) && get_ttyp ()
404 && !(get_ttyp ()->ti.c_lflag & NOFLSH))
405 tcflush (TCIFLUSH);
406 }
407
408 pid_t
409 fhandler_termios::tcgetsid ()
410 {
411 if (myself->ctty > 0 && myself->ctty == tc ()->ntty)
412 return tc ()->getsid ();
413 set_errno (ENOTTY);
414 return -1;
415 }
416
417 int
418 fhandler_termios::ioctl (int cmd, void *varg)
419 {
420 if (cmd != TIOCSCTTY)
421 return 1; /* Not handled by this function */
422
423 int arg = (int) varg;
424
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;
440 myself->set_ctty (this, 0);
441 return 0;
442 }
This page took 0.053251 seconds and 4 git commands to generate.