111
|
1 /****************************************************************************
|
|
2 * *
|
|
3 * GNAT RUN-TIME COMPONENTS *
|
|
4 * *
|
|
5 * T E R M I N A L S *
|
|
6 * *
|
|
7 * C Implementation File *
|
|
8 * *
|
145
|
9 * Copyright (C) 2008-2019, AdaCore *
|
111
|
10 * *
|
|
11 * GNAT is free software; you can redistribute it and/or modify it under *
|
|
12 * terms of the GNU General Public License as published by the Free Soft- *
|
|
13 * ware Foundation; either version 3, or (at your option) any later ver- *
|
|
14 * sion. GNAT is distributed in the hope that it will be useful, but WITH- *
|
|
15 * OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
|
|
16 * or FITNESS FOR A PARTICULAR PURPOSE. *
|
|
17 * *
|
|
18 * As a special exception under Section 7 of GPL version 3, you are granted *
|
|
19 * additional permissions described in the GCC Runtime Library Exception, *
|
|
20 * version 3.1, as published by the Free Software Foundation. *
|
|
21 * *
|
|
22 * You should have received a copy of the GNU General Public License and *
|
|
23 * a copy of the GCC Runtime Library Exception along with this program; *
|
|
24 * see the files COPYING3 and COPYING.RUNTIME respectively. If not, see *
|
|
25 * <http://www.gnu.org/licenses/>. *
|
|
26 * *
|
|
27 * GNAT was originally developed by the GNAT team at New York University. *
|
|
28 * Extensive contributions were provided by Ada Core Technologies Inc. *
|
|
29 * *
|
|
30 ****************************************************************************/
|
|
31
|
131
|
32 #define ATTRIBUTE_UNUSED __attribute__((unused))
|
|
33
|
111
|
34 /* First all usupported platforms. Add stubs for exported routines. */
|
|
35
|
|
36 #if defined (VMS) || defined (__vxworks) || defined (__Lynx__) \
|
|
37 || defined (__ANDROID__) || defined (__PikeOS__) || defined(__DJGPP__)
|
|
38
|
|
39 void *
|
|
40 __gnat_new_tty (void)
|
|
41 {
|
|
42 return (void*)0;
|
|
43 }
|
|
44
|
|
45 char *
|
|
46 __gnat_tty_name (void* t ATTRIBUTE_UNUSED)
|
|
47 {
|
|
48 return (char*)0;
|
|
49 }
|
|
50
|
|
51 int
|
|
52 __gnat_interrupt_pid (int pid ATTRIBUTE_UNUSED)
|
|
53 {
|
|
54 return -1;
|
|
55 }
|
|
56
|
|
57 int
|
|
58 __gnat_interrupt_process (void* desc ATTRIBUTE_UNUSED)
|
|
59 {
|
|
60 return -1;
|
|
61 }
|
|
62
|
|
63 int
|
|
64 __gnat_setup_communication (void** desc ATTRIBUTE_UNUSED)
|
|
65 {
|
|
66 return -1;
|
|
67 }
|
|
68
|
|
69 void
|
|
70 __gnat_setup_parent_communication (void *d ATTRIBUTE_UNUSED,
|
|
71 int *i ATTRIBUTE_UNUSED,
|
|
72 int *o ATTRIBUTE_UNUSED,
|
|
73 int *e ATTRIBUTE_UNUSED,
|
|
74 int *p ATTRIBUTE_UNUSED)
|
|
75 {
|
|
76 }
|
|
77
|
|
78 int
|
|
79 __gnat_setup_child_communication (void *d ATTRIBUTE_UNUSED,
|
|
80 char **n ATTRIBUTE_UNUSED,
|
|
81 int u ATTRIBUTE_UNUSED)
|
|
82 {
|
|
83 return -1;
|
|
84 }
|
|
85
|
|
86 int
|
|
87 __gnat_terminate_process (void *desc ATTRIBUTE_UNUSED)
|
|
88 {
|
|
89 return -1;
|
|
90 }
|
|
91
|
|
92 int
|
|
93 __gnat_terminate_pid (int pid ATTRIBUTE_UNUSED)
|
|
94 {
|
|
95 return -1;
|
|
96 }
|
|
97
|
|
98 int
|
|
99 __gnat_tty_fd (void* t ATTRIBUTE_UNUSED)
|
|
100 {
|
|
101 return -1;
|
|
102 }
|
|
103
|
|
104 int
|
|
105 __gnat_tty_supported (void)
|
|
106 {
|
|
107 return 0;
|
|
108 }
|
|
109
|
|
110 int
|
145
|
111 __gnat_tty_waitpid (void *desc ATTRIBUTE_UNUSED, int blocking)
|
111
|
112 {
|
|
113 return 1;
|
|
114 }
|
|
115
|
|
116 void
|
|
117 __gnat_close_tty (void* t ATTRIBUTE_UNUSED)
|
|
118 {
|
|
119 }
|
|
120
|
|
121 void
|
|
122 __gnat_free_process (void** process ATTRIBUTE_UNUSED)
|
|
123 {
|
|
124 }
|
|
125
|
|
126 void
|
|
127 __gnat_reset_tty (void* t ATTRIBUTE_UNUSED)
|
|
128 {
|
|
129 }
|
|
130
|
|
131 void
|
|
132 __gnat_send_header (void* d ATTRIBUTE_UNUSED,
|
|
133 char h[5] ATTRIBUTE_UNUSED,
|
|
134 int s ATTRIBUTE_UNUSED,
|
|
135 int *r ATTRIBUTE_UNUSED)
|
|
136 {
|
|
137 }
|
|
138
|
|
139 void
|
|
140 __gnat_setup_winsize (void *desc ATTRIBUTE_UNUSED,
|
|
141 int rows ATTRIBUTE_UNUSED,
|
|
142 int columns ATTRIBUTE_UNUSED)
|
|
143 {
|
|
144 }
|
|
145
|
|
146 /* For Windows platforms. */
|
|
147
|
|
148 #elif defined(_WIN32)
|
|
149
|
|
150 #include <errno.h>
|
|
151 #include <stdio.h>
|
|
152 #include <stdlib.h>
|
|
153
|
|
154 #include <windows.h>
|
145
|
155 #include <winternl.h>
|
111
|
156
|
|
157 #define MAXPATHLEN 1024
|
|
158
|
|
159 #define NILP(x) ((x) == 0)
|
|
160 #define Qnil 0
|
|
161 #define report_file_error(x, y) fprintf (stderr, "Error: %s\n", x);
|
|
162 #define INTEGERP(x) 1
|
|
163 #define XINT(x) x
|
|
164
|
|
165 struct TTY_Process {
|
|
166 int pid; /* Number of this process */
|
|
167 PROCESS_INFORMATION procinfo;
|
|
168 HANDLE w_infd, w_outfd;
|
|
169 HANDLE w_forkin, w_forkout;
|
|
170 BOOL usePipe;
|
|
171 };
|
|
172
|
145
|
173 /* Control whether create_child cause the process to inherit GNAT Studio'
|
111
|
174 error mode setting. The default is 1, to minimize the possibility of
|
|
175 subprocesses blocking when accessing unmounted drives. */
|
|
176 static int Vw32_start_process_inherit_error_mode = 1;
|
|
177
|
|
178 /* Control whether spawnve quotes arguments as necessary to ensure
|
|
179 correct parsing by child process. Because not all uses of spawnve
|
|
180 are careful about constructing argv arrays, we make this behavior
|
|
181 conditional (off by default, since a similar operation is already done
|
|
182 in g-expect.adb by calling Normalize_Argument). */
|
|
183 static int Vw32_quote_process_args = 0;
|
|
184
|
|
185 static DWORD AbsoluteSeek(HANDLE, DWORD);
|
|
186 static VOID ReadBytes(HANDLE, LPVOID, DWORD);
|
|
187
|
|
188 #define XFER_BUFFER_SIZE 2048
|
|
189
|
|
190 /* This tell if the executable we're about to launch uses a GUI interface. */
|
|
191 /* if we can't determine it, we will return true */
|
|
192 static int
|
|
193 is_gui_app (char *exe)
|
|
194 {
|
|
195 HANDLE hImage;
|
|
196
|
|
197 DWORD bytes;
|
|
198 DWORD iSection;
|
|
199 DWORD SectionOffset;
|
|
200 DWORD CoffHeaderOffset;
|
|
201 DWORD MoreDosHeader[16];
|
|
202 CHAR *file;
|
|
203 size_t nlen;
|
|
204
|
|
205 ULONG ntSignature;
|
|
206
|
|
207 IMAGE_DOS_HEADER image_dos_header;
|
|
208 IMAGE_FILE_HEADER image_file_header;
|
|
209 IMAGE_OPTIONAL_HEADER image_optional_header;
|
|
210 IMAGE_SECTION_HEADER image_section_header;
|
|
211
|
|
212 /*
|
|
213 * Open the reference file.
|
|
214 */
|
|
215 nlen = strlen (exe);
|
|
216 file = exe;
|
|
217 if (nlen > 2) {
|
|
218 if (exe[0] == '"') {
|
|
219 /* remove quotes */
|
|
220 nlen -= 2;
|
|
221 file = malloc ((nlen + 1) * sizeof (char));
|
|
222 memcpy (file, &exe[1], nlen);
|
|
223 file [nlen] = '\0';
|
|
224 }
|
|
225 }
|
|
226 hImage = CreateFile(file,
|
|
227 GENERIC_READ,
|
|
228 FILE_SHARE_READ,
|
|
229 NULL,
|
|
230 OPEN_EXISTING,
|
|
231 FILE_ATTRIBUTE_NORMAL,
|
|
232 NULL);
|
|
233
|
|
234 if (file != exe) {
|
|
235 free (file);
|
|
236 }
|
|
237
|
|
238 if (INVALID_HANDLE_VALUE == hImage)
|
|
239 {
|
|
240 report_file_error ("Could not open exe: ", Qnil);
|
|
241 report_file_error (exe, Qnil);
|
|
242 report_file_error ("\n", Qnil);
|
|
243 CloseHandle (hImage);
|
|
244 return -1;
|
|
245 }
|
|
246
|
|
247 /*
|
|
248 * Read the MS-DOS image header.
|
|
249 */
|
|
250 ReadBytes(hImage, &image_dos_header, sizeof(IMAGE_DOS_HEADER));
|
|
251
|
|
252 if (IMAGE_DOS_SIGNATURE != image_dos_header.e_magic)
|
|
253 {
|
|
254 report_file_error("Sorry, I do not understand this file.\n", Qnil);
|
|
255 CloseHandle (hImage);
|
|
256 return -1;
|
|
257 }
|
|
258
|
|
259 /*
|
|
260 * Read more MS-DOS header. */
|
|
261 ReadBytes(hImage, MoreDosHeader, sizeof(MoreDosHeader));
|
|
262 /*
|
|
263 * Get actual COFF header.
|
|
264 */
|
|
265 CoffHeaderOffset = AbsoluteSeek(hImage, image_dos_header.e_lfanew) +
|
|
266 sizeof(ULONG);
|
|
267 if (CoffHeaderOffset < 0) {
|
|
268 CloseHandle (hImage);
|
|
269 return -1;
|
|
270 }
|
|
271
|
|
272 ReadBytes (hImage, &ntSignature, sizeof(ULONG));
|
|
273
|
|
274 if (IMAGE_NT_SIGNATURE != ntSignature)
|
|
275 {
|
|
276 report_file_error ("Missing NT signature. Unknown file type.\n", Qnil);
|
|
277 CloseHandle (hImage);
|
|
278 return -1;
|
|
279 }
|
|
280
|
|
281 SectionOffset = CoffHeaderOffset + IMAGE_SIZEOF_FILE_HEADER +
|
|
282 IMAGE_SIZEOF_NT_OPTIONAL_HEADER;
|
|
283
|
|
284 ReadBytes(hImage, &image_file_header, IMAGE_SIZEOF_FILE_HEADER);
|
|
285
|
|
286 /*
|
|
287 * Read optional header.
|
|
288 */
|
|
289 ReadBytes(hImage,
|
|
290 &image_optional_header,
|
|
291 IMAGE_SIZEOF_NT_OPTIONAL_HEADER);
|
|
292
|
|
293 CloseHandle (hImage);
|
|
294
|
|
295 switch (image_optional_header.Subsystem)
|
|
296 {
|
|
297 case IMAGE_SUBSYSTEM_UNKNOWN:
|
|
298 return 1;
|
|
299
|
|
300 case IMAGE_SUBSYSTEM_NATIVE:
|
|
301 return 1;
|
|
302
|
|
303 case IMAGE_SUBSYSTEM_WINDOWS_GUI:
|
|
304 return 1;
|
|
305
|
|
306 case IMAGE_SUBSYSTEM_WINDOWS_CUI:
|
|
307 return 0;
|
|
308
|
|
309 case IMAGE_SUBSYSTEM_OS2_CUI:
|
|
310 return 0;
|
|
311
|
|
312 case IMAGE_SUBSYSTEM_POSIX_CUI:
|
|
313 return 0;
|
|
314
|
|
315 default:
|
|
316 /* Unknown, return GUI app to be preservative: if yes, it will be
|
|
317 correctly launched, if no, it will be launched, and a console will
|
|
318 be also displayed, which is not a big deal */
|
|
319 return 1;
|
|
320 }
|
|
321
|
|
322 }
|
|
323
|
|
324 static DWORD
|
|
325 AbsoluteSeek (HANDLE hFile, DWORD offset)
|
|
326 {
|
|
327 DWORD newOffset;
|
|
328
|
|
329 newOffset = SetFilePointer (hFile, offset, NULL, FILE_BEGIN);
|
|
330
|
|
331 if (newOffset == 0xFFFFFFFF)
|
|
332 return -1;
|
|
333 else
|
|
334 return newOffset;
|
|
335 }
|
|
336
|
|
337 static VOID
|
|
338 ReadBytes (HANDLE hFile, LPVOID buffer, DWORD size)
|
|
339 {
|
|
340 DWORD bytes;
|
|
341
|
|
342 if (!ReadFile(hFile, buffer, size, &bytes, NULL))
|
|
343 {
|
|
344 size = 0;
|
|
345 return;
|
|
346 }
|
|
347 else if (size != bytes)
|
|
348 {
|
|
349 return;
|
|
350 }
|
|
351 }
|
|
352
|
|
353 static int
|
|
354 nt_spawnve (char *exe, char **argv, char *env, struct TTY_Process *process)
|
|
355 {
|
|
356 STARTUPINFO start;
|
|
357 SECURITY_ATTRIBUTES sec_attrs;
|
|
358 SECURITY_DESCRIPTOR sec_desc;
|
|
359 DWORD flags;
|
|
360 char dir[ MAXPATHLEN ];
|
|
361 int pid;
|
|
362 int is_gui, use_cmd;
|
|
363 char *cmdline, *parg, **targ;
|
|
364 int do_quoting = 0;
|
|
365 char escape_char;
|
|
366 int arglen;
|
|
367
|
|
368 /* we have to do some conjuring here to put argv and envp into the
|
|
369 form CreateProcess wants... argv needs to be a space separated/null
|
|
370 terminated list of parameters, and envp is a null
|
|
371 separated/double-null terminated list of parameters.
|
|
372
|
|
373 Additionally, zero-length args and args containing whitespace or
|
|
374 quote chars need to be wrapped in double quotes - for this to work,
|
|
375 embedded quotes need to be escaped as well. The aim is to ensure
|
|
376 the child process reconstructs the argv array we start with
|
|
377 exactly, so we treat quotes at the beginning and end of arguments
|
|
378 as embedded quotes.
|
|
379
|
|
380 Note that using backslash to escape embedded quotes requires
|
|
381 additional special handling if an embedded quote is already
|
|
382 preceded by backslash, or if an arg requiring quoting ends with
|
|
383 backslash. In such cases, the run of escape characters needs to be
|
|
384 doubled. For consistency, we apply this special handling as long
|
|
385 as the escape character is not quote.
|
|
386
|
|
387 Since we have no idea how large argv and envp are likely to be we
|
|
388 figure out list lengths on the fly and allocate them. */
|
|
389
|
|
390 if (!NILP (Vw32_quote_process_args))
|
|
391 {
|
|
392 do_quoting = 1;
|
|
393 /* Override escape char by binding w32-quote-process-args to
|
|
394 desired character, or use t for auto-selection. */
|
|
395 if (INTEGERP (Vw32_quote_process_args))
|
|
396 escape_char = XINT (Vw32_quote_process_args);
|
|
397 else
|
|
398 escape_char = '\\';
|
|
399 }
|
|
400
|
|
401 /* do argv... */
|
|
402 arglen = 0;
|
|
403 targ = argv;
|
|
404 while (*targ)
|
|
405 {
|
|
406 char *p = *targ;
|
|
407 int need_quotes = 0;
|
|
408 int escape_char_run = 0;
|
|
409
|
|
410 if (*p == 0)
|
|
411 need_quotes = 1;
|
|
412 for ( ; *p; p++)
|
|
413 {
|
|
414 if (*p == '"')
|
|
415 {
|
|
416 /* allow for embedded quotes to be escaped */
|
|
417 arglen++;
|
|
418 need_quotes = 1;
|
|
419 /* handle the case where the embedded quote is already escaped */
|
|
420 if (escape_char_run > 0)
|
|
421 {
|
|
422 /* To preserve the arg exactly, we need to double the
|
|
423 preceding escape characters (plus adding one to
|
|
424 escape the quote character itself). */
|
|
425 arglen += escape_char_run;
|
|
426 }
|
|
427 }
|
|
428 else if (*p == ' ' || *p == '\t')
|
|
429 {
|
|
430 need_quotes = 1;
|
|
431 }
|
|
432
|
|
433 if (*p == escape_char && escape_char != '"')
|
|
434 escape_char_run++;
|
|
435 else
|
|
436 escape_char_run = 0;
|
|
437 }
|
|
438 if (need_quotes)
|
|
439 {
|
|
440 arglen += 2;
|
|
441 /* handle the case where the arg ends with an escape char - we
|
|
442 must not let the enclosing quote be escaped. */
|
|
443 if (escape_char_run > 0)
|
|
444 arglen += escape_char_run;
|
|
445 }
|
|
446 arglen += strlen (*targ) + 1;
|
|
447 targ++;
|
|
448 }
|
|
449
|
|
450 is_gui = is_gui_app (argv[0]);
|
|
451 use_cmd = FALSE;
|
|
452
|
|
453 if (is_gui == -1) {
|
|
454 /* could not determine application type. Try launching with "cmd /c" */
|
|
455 is_gui = FALSE;
|
|
456 arglen += 7;
|
|
457 use_cmd = TRUE;
|
|
458 }
|
|
459
|
|
460 cmdline = (char*)malloc (arglen + 1);
|
|
461 targ = argv;
|
|
462 parg = cmdline;
|
|
463
|
|
464 if (use_cmd == TRUE) {
|
|
465 strcpy (parg, "cmd /c ");
|
|
466 parg += 7;
|
|
467 }
|
|
468
|
|
469 while (*targ)
|
|
470 {
|
|
471 char * p = *targ;
|
|
472 int need_quotes = 0;
|
|
473
|
|
474 if (*p == 0)
|
|
475 need_quotes = 1;
|
|
476
|
|
477 if (do_quoting)
|
|
478 {
|
|
479 for ( ; *p; p++)
|
|
480 if (*p == ' ' || *p == '\t' || *p == '"')
|
|
481 need_quotes = 1;
|
|
482 }
|
|
483 if (need_quotes)
|
|
484 {
|
|
485 int escape_char_run = 0;
|
|
486 char * first;
|
|
487 char * last;
|
|
488
|
|
489 p = *targ;
|
|
490 first = p;
|
|
491 last = p + strlen (p) - 1;
|
|
492 *parg++ = '"';
|
|
493 for ( ; *p; p++)
|
|
494 {
|
|
495 if (*p == '"')
|
|
496 {
|
|
497 /* double preceding escape chars if any */
|
|
498 while (escape_char_run > 0)
|
|
499 {
|
|
500 *parg++ = escape_char;
|
|
501 escape_char_run--;
|
|
502 }
|
|
503 /* escape all quote chars, even at beginning or end */
|
|
504 *parg++ = escape_char;
|
|
505 }
|
|
506 *parg++ = *p;
|
|
507
|
|
508 if (*p == escape_char && escape_char != '"')
|
|
509 escape_char_run++;
|
|
510 else
|
|
511 escape_char_run = 0;
|
|
512 }
|
|
513 /* double escape chars before enclosing quote */
|
|
514 while (escape_char_run > 0)
|
|
515 {
|
|
516 *parg++ = escape_char;
|
|
517 escape_char_run--;
|
|
518 }
|
|
519 *parg++ = '"';
|
|
520 }
|
|
521 else
|
|
522 {
|
|
523 strcpy (parg, *targ);
|
|
524 parg += strlen (*targ);
|
|
525 }
|
|
526 *parg++ = ' ';
|
|
527 targ++;
|
|
528 }
|
|
529 *--parg = '\0';
|
|
530
|
|
531 memset (&start, 0, sizeof (start));
|
|
532 start.cb = sizeof (start);
|
|
533
|
|
534 if (process->usePipe == TRUE) {
|
|
535 start.dwFlags = STARTF_USESTDHANDLES;
|
|
536 start.hStdInput = process->w_forkin;
|
|
537 start.hStdOutput = process->w_forkout;
|
|
538 /* child's stderr is always redirected to outfd */
|
|
539 start.hStdError = process->w_forkout;
|
|
540 } else {
|
|
541 start.dwFlags = STARTF_USESTDHANDLES;
|
|
542 /* We only need to redirect stderr/stdout here. Stdin will be forced to
|
|
543 the spawned process console by explaunch */
|
|
544 start.hStdInput = NULL;
|
|
545 start.hStdOutput = process->w_forkout;
|
|
546 start.hStdError = process->w_forkout;
|
|
547 }
|
|
548
|
|
549 /* Explicitly specify no security */
|
|
550 if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
|
|
551 goto EH_Fail;
|
|
552 if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
|
|
553 goto EH_Fail;
|
|
554 sec_attrs.nLength = sizeof (sec_attrs);
|
|
555 sec_attrs.lpSecurityDescriptor = &sec_desc;
|
|
556 sec_attrs.bInheritHandle = FALSE;
|
|
557
|
|
558 /* creating a new console allow easier close. Do not use
|
|
559 CREATE_NEW_PROCESS_GROUP as this results in disabling Ctrl+C */
|
|
560 flags = CREATE_NEW_CONSOLE;
|
|
561 if (NILP (Vw32_start_process_inherit_error_mode))
|
|
562 flags |= CREATE_DEFAULT_ERROR_MODE;
|
|
563
|
|
564 /* if app is not a gui application, hide the console */
|
|
565 if (is_gui == FALSE) {
|
|
566 start.dwFlags |= STARTF_USESHOWWINDOW;
|
|
567 start.wShowWindow = SW_HIDE;
|
|
568 }
|
|
569
|
|
570 /* Set initial directory to null character to use current directory */
|
|
571 if (!CreateProcess (NULL, cmdline, &sec_attrs, NULL, TRUE,
|
|
572 flags, env, NULL, &start, &process->procinfo))
|
|
573 goto EH_Fail;
|
|
574
|
|
575 pid = (int) process->procinfo.hProcess;
|
|
576 process->pid=pid;
|
|
577
|
|
578 return pid;
|
|
579
|
|
580 EH_Fail:
|
|
581 return -1;
|
|
582 }
|
|
583
|
|
584 /*************************
|
|
585 ** __gnat_send_header ()
|
|
586 *************************/
|
|
587
|
|
588 #define EXP_SLAVE_CREATE 'c'
|
|
589 #define EXP_SLAVE_KEY 'k'
|
|
590 #define EXP_SLAVE_MOUSE 'm'
|
|
591 #define EXP_SLAVE_WRITE 'w'
|
|
592 #define EXP_SLAVE_KILL 'x'
|
|
593
|
|
594 #define EXP_KILL_TERMINATE 0x1
|
|
595 #define EXP_KILL_CTRL_C 0x2
|
|
596 #define EXP_KILL_CTRL_BREAK 0x4
|
|
597
|
|
598 void
|
|
599 __gnat_send_header (struct TTY_Process* p, char header[5], int size, int *ret)
|
|
600 {
|
|
601 if (p->usePipe == FALSE) {
|
|
602 header[0] = EXP_SLAVE_WRITE;
|
|
603 header[1] = size & 0xff;
|
|
604 header[2] = (size & 0xff00) >> 8;
|
|
605 header[3] = (size & 0xff0000) >> 16;
|
|
606 header[4] = (size & 0xff000000) >> 24;
|
|
607 *ret = 1;
|
|
608 } else {
|
|
609 *ret = 0;
|
|
610 }
|
|
611 }
|
|
612
|
|
613 /**********************************
|
|
614 ** __gnat_setup_communication ()
|
|
615 **********************************/
|
|
616
|
|
617 int
|
|
618 __gnat_setup_communication (struct TTY_Process** process_out) /* output param */
|
|
619 {
|
|
620 struct TTY_Process* process;
|
|
621
|
|
622 process = (struct TTY_Process*)malloc (sizeof (struct TTY_Process));
|
|
623 ZeroMemory (process, sizeof (struct TTY_Process));
|
|
624 *process_out = process;
|
|
625
|
|
626 return 0;
|
|
627 }
|
|
628
|
|
629 #define EXP_PIPE_BASENAME "\\\\.\\pipe\\ExpectPipe"
|
|
630
|
|
631 int
|
|
632 __gnat_setup_child_communication
|
|
633 (struct TTY_Process* process,
|
|
634 char** argv,
|
|
635 int Use_Pipes)
|
|
636 {
|
|
637 int cpid;
|
|
638 HANDLE parent;
|
|
639 SECURITY_ATTRIBUTES sec_attrs;
|
|
640 char slavePath [MAX_PATH];
|
|
641 char **nargv;
|
|
642 int argc;
|
|
643 int i;
|
|
644 char pipeNameIn[100];
|
|
645 HANDLE hSlaveInDrv = NULL; /* Handle to communicate with slave driver */
|
|
646
|
|
647 parent = GetCurrentProcess ();
|
|
648
|
|
649 /* Set inheritance for the pipe handles */
|
|
650 sec_attrs.nLength = sizeof (SECURITY_ATTRIBUTES);
|
|
651 sec_attrs.bInheritHandle = TRUE;
|
|
652 sec_attrs.lpSecurityDescriptor = NULL;
|
|
653
|
|
654 if (Use_Pipes) {
|
|
655 /* Create in and out pipes */
|
|
656 if (!CreatePipe (&process->w_forkin, &process->w_infd, &sec_attrs, 0))
|
|
657 report_file_error ("Creation of child's IN handle", Qnil);
|
|
658 if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
|
|
659 report_file_error ("Creation of child's OUT handle", Qnil);
|
|
660
|
|
661 /* Do not inherit the parent's side of the pipes */
|
|
662 SetHandleInformation (&process->w_infd, HANDLE_FLAG_INHERIT, 0);
|
|
663 SetHandleInformation (&process->w_outfd, HANDLE_FLAG_INHERIT, 0);
|
|
664
|
|
665 /* use native argv */
|
|
666 nargv = argv;
|
|
667 process->usePipe = TRUE;
|
|
668
|
|
669 } else {
|
|
670 static int pipeNameId = 0;
|
|
671
|
|
672 process->w_infd = NULL;
|
|
673
|
|
674 /* We create a named pipe for Input, as we handle input by sending special
|
|
675 commands to the explaunch process, that uses it to feed the actual input
|
|
676 of the process */
|
|
677 sprintf(pipeNameIn, "%sIn%08x_%08x", EXP_PIPE_BASENAME,
|
|
678 GetCurrentProcessId(), pipeNameId);
|
|
679 pipeNameId++;
|
|
680
|
|
681 hSlaveInDrv = CreateNamedPipe(pipeNameIn,
|
|
682 PIPE_ACCESS_OUTBOUND,
|
|
683 PIPE_TYPE_BYTE | PIPE_WAIT, 1, 8192, 8192,
|
|
684 20000, NULL);
|
|
685 if (hSlaveInDrv == NULL) goto end;
|
|
686
|
|
687 if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
|
|
688 report_file_error ("Creation of child's OUT handle", Qnil);
|
|
689
|
|
690 if (SearchPath (NULL, "explaunch.exe", NULL,
|
|
691 MAX_PATH, slavePath, NULL) == 0) goto end;
|
|
692
|
|
693 for (argc=0; argv[argc] != NULL; argc++) ;
|
|
694 nargv = (char **) malloc (sizeof (char*) * (argc + 3));
|
|
695 nargv[0] = slavePath;
|
|
696 nargv[1] = pipeNameIn;
|
|
697
|
|
698 for (i = 0; i <= argc; i++) nargv[i + 2] = argv[i];
|
|
699 process->usePipe = FALSE;
|
|
700 }
|
|
701
|
|
702 /* Spawn the child. */
|
|
703 cpid = nt_spawnve (nargv[0], nargv, NULL, process);
|
|
704
|
|
705 /* close the duplicated handles passed to the child */
|
|
706 CloseHandle (process->w_forkout);
|
|
707
|
|
708 if (process->usePipe == TRUE) {
|
|
709 CloseHandle (process->w_forkin);
|
|
710
|
|
711 } else {
|
|
712 UCHAR buf[8]; /* enough space for child status info */
|
|
713 DWORD count;
|
|
714 BOOL bRet;
|
|
715 DWORD dwRet;
|
|
716
|
|
717 /*
|
|
718 * Wait for connection with the slave driver
|
|
719 */
|
|
720 bRet = ConnectNamedPipe(hSlaveInDrv, NULL);
|
|
721 if (bRet == FALSE) {
|
|
722 dwRet = GetLastError();
|
|
723 if (dwRet == ERROR_PIPE_CONNECTED) {
|
|
724 ;
|
|
725 } else {
|
|
726 goto end;
|
|
727 }
|
|
728 }
|
|
729
|
|
730 process->w_infd = hSlaveInDrv;
|
|
731
|
|
732 /*
|
|
733 * wait for slave driver to initialize before allowing user to send to it
|
|
734 */
|
|
735 bRet = ReadFile(process->w_outfd, buf, 8, &count, NULL);
|
|
736 if (bRet == FALSE) {
|
|
737 cpid = -1;
|
|
738 }
|
|
739
|
|
740 dwRet = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
|
741 if (dwRet != 0) {
|
|
742 cpid = -1;
|
|
743 }
|
|
744
|
|
745 cpid = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24);
|
|
746 process->pid = cpid;
|
|
747 }
|
|
748
|
|
749 if (cpid == -1)
|
|
750 /* An error occurred while trying to spawn the process. */
|
|
751 report_file_error ("Spawning child process", Qnil);
|
|
752
|
|
753 return cpid;
|
|
754 end:
|
|
755 if (hSlaveInDrv != NULL)
|
|
756 CloseHandle (hSlaveInDrv);
|
|
757 return -1;
|
|
758 }
|
|
759
|
|
760 void
|
|
761 __gnat_setup_parent_communication
|
|
762 (struct TTY_Process* process,
|
|
763 int* in,
|
|
764 int* out,
|
|
765 int* err,
|
|
766 int* pid)
|
|
767 {
|
|
768 *in = _open_osfhandle ((long) process->w_infd, 0);
|
|
769 *out = _open_osfhandle ((long) process->w_outfd, 0);
|
|
770 /* child's stderr is always redirected to outfd */
|
|
771 *err = *out;
|
|
772 *pid = process->pid;
|
|
773 }
|
|
774
|
|
775 typedef struct _child_process
|
|
776 {
|
|
777 HWND hwnd;
|
|
778 PROCESS_INFORMATION *procinfo;
|
|
779 } child_process;
|
|
780
|
|
781 /* The major and minor versions of NT. */
|
|
782 static int w32_major_version;
|
|
783 static int w32_minor_version;
|
|
784
|
|
785 /* Distinguish between Windows NT and Windows 95. */
|
|
786 static enum {OS_UNKNOWN, OS_WIN95, OS_NT} os_subtype = OS_UNKNOWN;
|
|
787
|
|
788 /* Cache information describing the NT system for later use. */
|
|
789 static void
|
|
790 cache_system_info (void)
|
|
791 {
|
|
792 union
|
|
793 {
|
|
794 struct info
|
|
795 {
|
|
796 char major;
|
|
797 char minor;
|
|
798 short platform;
|
|
799 } info;
|
|
800 DWORD data;
|
|
801 } version;
|
|
802
|
|
803 /* Cache the version of the operating system. */
|
|
804 version.data = GetVersion ();
|
|
805 w32_major_version = version.info.major;
|
|
806 w32_minor_version = version.info.minor;
|
|
807
|
|
808 if (version.info.platform & 0x8000)
|
|
809 os_subtype = OS_WIN95;
|
|
810 else
|
|
811 os_subtype = OS_NT;
|
|
812 }
|
|
813
|
|
814 static BOOL CALLBACK
|
|
815 find_child_console (HWND hwnd, child_process * cp)
|
|
816 {
|
|
817 DWORD thread_id;
|
|
818 DWORD process_id;
|
|
819
|
|
820 thread_id = GetWindowThreadProcessId (hwnd, &process_id);
|
|
821 if (process_id == cp->procinfo->dwProcessId)
|
|
822 {
|
|
823 char window_class[32];
|
|
824
|
|
825 GetClassName (hwnd, window_class, sizeof (window_class));
|
|
826 if (strcmp (window_class,
|
|
827 (os_subtype == OS_WIN95)
|
|
828 ? "tty"
|
|
829 : "ConsoleWindowClass") == 0)
|
|
830 {
|
|
831 cp->hwnd = hwnd;
|
|
832 return FALSE;
|
|
833 }
|
|
834 }
|
|
835 /* keep looking */
|
|
836 return TRUE;
|
|
837 }
|
|
838
|
|
839 int
|
|
840 __gnat_interrupt_process (struct TTY_Process* p)
|
|
841 {
|
|
842 char buf[2];
|
|
843 DWORD written;
|
|
844 BOOL bret;
|
|
845
|
|
846 if (p->usePipe == TRUE) {
|
|
847 bret = FALSE;
|
|
848 } else {
|
|
849 buf[0] = EXP_SLAVE_KILL;
|
|
850 buf[1] = EXP_KILL_CTRL_C;
|
|
851 bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
|
|
852 }
|
|
853
|
|
854 if (bret == FALSE) {
|
|
855 return __gnat_interrupt_pid (p->procinfo.dwProcessId);
|
|
856 }
|
|
857 return 0;
|
|
858 }
|
|
859
|
|
860 int
|
|
861 __gnat_interrupt_pid (int pid)
|
|
862 {
|
|
863 volatile child_process cp;
|
|
864 int rc = 0;
|
|
865
|
|
866 cp.procinfo = (LPPROCESS_INFORMATION) malloc (sizeof (PROCESS_INFORMATION));
|
|
867 cp.procinfo->dwProcessId = pid;
|
|
868
|
|
869 if (os_subtype == OS_UNKNOWN)
|
|
870 cache_system_info ();
|
|
871
|
|
872 /* Try to locate console window for process. */
|
|
873 EnumWindows ((WNDENUMPROC) find_child_console, (LPARAM) &cp);
|
|
874
|
|
875 if (cp.hwnd)
|
|
876 {
|
|
877 BYTE control_scan_code = (BYTE) MapVirtualKey (VK_CONTROL, 0);
|
|
878 /* Retrieve Ctrl-C scancode */
|
|
879 BYTE vk_break_code = 'C';
|
|
880 BYTE break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0);
|
|
881 HWND foreground_window;
|
|
882
|
|
883 foreground_window = GetForegroundWindow ();
|
|
884 if (foreground_window)
|
|
885 {
|
|
886 /* NT 5.0, and apparently also Windows 98, will not allow
|
|
887 a Window to be set to foreground directly without the
|
|
888 user's involvement. The workaround is to attach
|
|
889 ourselves to the thread that owns the foreground
|
|
890 window, since that is the only thread that can set the
|
|
891 foreground window. */
|
|
892 DWORD foreground_thread, child_thread;
|
|
893
|
|
894 foreground_thread =
|
|
895 GetWindowThreadProcessId (foreground_window, NULL);
|
|
896 if (foreground_thread == GetCurrentThreadId ()
|
|
897 || !AttachThreadInput (GetCurrentThreadId (),
|
|
898 foreground_thread, TRUE))
|
|
899 foreground_thread = 0;
|
|
900
|
|
901 child_thread = GetWindowThreadProcessId (cp.hwnd, NULL);
|
|
902 if (child_thread == GetCurrentThreadId ()
|
|
903 || !AttachThreadInput (GetCurrentThreadId (),
|
|
904 child_thread, TRUE))
|
|
905 child_thread = 0;
|
|
906
|
|
907 /* Set the foreground window to the child. */
|
|
908 if (SetForegroundWindow (cp.hwnd))
|
|
909 {
|
|
910 /* Generate keystrokes as if user had typed Ctrl-Break or
|
|
911 Ctrl-C. */
|
|
912 keybd_event (VK_CONTROL, control_scan_code, 0, 0);
|
|
913 keybd_event (vk_break_code, break_scan_code,
|
|
914 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY), 0);
|
|
915 keybd_event (vk_break_code, break_scan_code,
|
|
916 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY)
|
|
917 | KEYEVENTF_KEYUP, 0);
|
|
918 keybd_event (VK_CONTROL, control_scan_code, KEYEVENTF_KEYUP, 0);
|
|
919
|
|
920 /* Sleep for a bit to give time for the main frame to respond
|
|
921 to focus change events. */
|
|
922 Sleep (100);
|
|
923
|
|
924 SetForegroundWindow (foreground_window);
|
|
925 }
|
|
926 /* Detach from the foreground and child threads now that
|
|
927 the foreground switching is over. */
|
|
928 if (foreground_thread)
|
|
929 AttachThreadInput (GetCurrentThreadId (), foreground_thread, FALSE);
|
|
930 if (child_thread)
|
|
931 AttachThreadInput (GetCurrentThreadId (), child_thread, FALSE);
|
|
932 }
|
|
933 }
|
|
934 /* Ctrl-Break is NT equivalent of SIGINT. */
|
|
935 else if (!GenerateConsoleCtrlEvent
|
|
936 (CTRL_BREAK_EVENT, cp.procinfo->dwProcessId))
|
|
937 {
|
|
938 errno = EINVAL;
|
|
939 rc = -1;
|
|
940 }
|
|
941
|
|
942 free (cp.procinfo);
|
|
943 return rc;
|
|
944 }
|
|
945
|
|
946 /* kill a process, as this implementation use CreateProcess on Win32 we need
|
|
947 to use Win32 TerminateProcess API */
|
|
948 int
|
|
949 __gnat_terminate_process (struct TTY_Process* p)
|
|
950 {
|
|
951 char buf[2];
|
|
952 DWORD written;
|
|
953 BOOL bret;
|
|
954
|
|
955 if (p->usePipe == TRUE) {
|
|
956 bret = FALSE;
|
|
957 } else {
|
|
958 buf[0] = EXP_SLAVE_KILL;
|
|
959 buf[1] = EXP_KILL_TERMINATE;
|
|
960 bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
|
|
961 }
|
|
962
|
|
963 if (bret == FALSE) {
|
|
964 if (!TerminateProcess (p->procinfo.hProcess, 1))
|
|
965 return -1;
|
|
966 else
|
|
967 return 0;
|
|
968 } else
|
|
969 return 0;
|
|
970 }
|
|
971
|
|
972 typedef struct {
|
|
973 DWORD dwProcessId;
|
|
974 HANDLE hwnd;
|
|
975 } pid_struct;
|
|
976
|
|
977 static BOOL CALLBACK
|
|
978 find_process_handle (HWND hwnd, pid_struct * ps)
|
|
979 {
|
|
980 DWORD thread_id;
|
|
981 DWORD process_id;
|
|
982
|
|
983 thread_id = GetWindowThreadProcessId (hwnd, &process_id);
|
|
984 if (process_id == ps->dwProcessId)
|
|
985 {
|
|
986 ps->hwnd = hwnd;
|
|
987 return FALSE;
|
|
988 }
|
|
989 /* keep looking */
|
|
990 return TRUE;
|
|
991 }
|
|
992
|
|
993 int
|
|
994 __gnat_terminate_pid (int pid)
|
|
995 {
|
|
996 pid_struct ps;
|
|
997
|
|
998 ps.dwProcessId = pid;
|
|
999 ps.hwnd = 0;
|
|
1000 EnumWindows ((WNDENUMPROC) find_process_handle, (LPARAM) &ps);
|
|
1001
|
|
1002 if (ps.hwnd)
|
|
1003 {
|
|
1004 if (!TerminateProcess (ps.hwnd, 1))
|
|
1005 return -1;
|
|
1006 else
|
|
1007 return 0;
|
|
1008 }
|
|
1009
|
|
1010 return -1;
|
|
1011 }
|
|
1012
|
|
1013 /* wait for process pid to terminate and return the process status. This
|
|
1014 implementation is different from the adaint.c one for Windows as it uses
|
|
1015 the Win32 API instead of the C one. */
|
|
1016
|
|
1017 int
|
145
|
1018 __gnat_tty_waitpid (struct TTY_Process* p, int blocking)
|
111
|
1019 {
|
|
1020 DWORD exitcode;
|
145
|
1021 HANDLE hprocess = p->procinfo.hProcess;
|
|
1022
|
|
1023 if (blocking) {
|
|
1024 /* Wait is needed on Windows only in blocking mode. */
|
|
1025 WaitForSingleObject (hprocess, 0);
|
|
1026 }
|
|
1027
|
|
1028 GetExitCodeProcess (hprocess, &exitcode);
|
111
|
1029
|
145
|
1030 if (exitcode == STILL_ACTIVE) {
|
|
1031 /* If process is still active return -1. */
|
|
1032 exitcode = -1;
|
|
1033 } else {
|
|
1034 /* Process is dead, so handle to process and main thread can be closed. */
|
|
1035 CloseHandle (p->procinfo.hThread);
|
|
1036 CloseHandle (hprocess);
|
|
1037 }
|
111
|
1038
|
|
1039 /* No need to close the handles: they were closed on the ada side */
|
|
1040 return (int) exitcode;
|
|
1041 }
|
|
1042
|
|
1043 /********************************
|
|
1044 ** __gnat_free_process ()
|
|
1045 ********************************/
|
|
1046
|
|
1047 void
|
|
1048 __gnat_free_process (struct TTY_Process** process)
|
|
1049 {
|
|
1050 free (*process);
|
|
1051 *process = NULL;
|
|
1052 }
|
|
1053
|
|
1054 /* TTY handling */
|
|
1055
|
|
1056 typedef struct {
|
|
1057 int tty_fd; /* descriptor for the tty */
|
|
1058 char tty_name[24]; /* Name of TTY device */
|
|
1059 } TTY_Handle;
|
|
1060
|
|
1061 int
|
|
1062 __gnat_tty_supported (void)
|
|
1063 {
|
|
1064 return 0;
|
|
1065 }
|
|
1066
|
|
1067 /* Return the tty name associated with p */
|
|
1068
|
|
1069 char *
|
|
1070 __gnat_tty_name (TTY_Handle* t)
|
|
1071 {
|
|
1072 return t->tty_name;
|
|
1073 }
|
|
1074
|
|
1075 int
|
|
1076 __gnat_tty_fd (TTY_Handle* t)
|
|
1077 {
|
|
1078 return t->tty_fd;
|
|
1079 }
|
|
1080
|
|
1081 TTY_Handle*
|
|
1082 __gnat_new_tty (void)
|
|
1083 {
|
|
1084 return (TTY_Handle*)0;
|
|
1085 }
|
|
1086
|
|
1087 void
|
|
1088 __gnat_reset_tty (TTY_Handle* t)
|
|
1089 {
|
|
1090 return;
|
|
1091 }
|
|
1092
|
|
1093 void
|
|
1094 __gnat_close_tty (TTY_Handle* t)
|
|
1095 {
|
|
1096 free (t);
|
|
1097 }
|
|
1098
|
|
1099 void
|
|
1100 __gnat_setup_winsize (void *desc, int rows, int columns)
|
|
1101 {
|
|
1102 }
|
|
1103
|
|
1104 #else /* defined(_WIN32, implementatin for all UNIXes */
|
|
1105
|
|
1106 /* First defined some macro to identify easily some systems */
|
|
1107 #if defined (__FreeBSD__) \
|
|
1108 || defined (__OpenBSD__) \
|
|
1109 || defined (__NetBSD__) \
|
|
1110 || defined (__DragonFly__)
|
|
1111 # define BSD
|
|
1112 #endif
|
|
1113
|
|
1114 /* Include every system header we need */
|
|
1115 #define _GNU_SOURCE
|
|
1116 #include <errno.h>
|
|
1117 #include <stdio.h>
|
|
1118 #include <stdlib.h>
|
|
1119 #include <sys/ioctl.h>
|
|
1120 #include <termios.h>
|
|
1121 #include <fcntl.h>
|
|
1122 #include <string.h>
|
|
1123 #include <sys/stat.h>
|
|
1124 #include <sys/types.h>
|
|
1125 #include <sys/wait.h>
|
|
1126 #include <unistd.h>
|
|
1127 #if defined (__sun__)
|
|
1128 # include <sys/stropts.h>
|
|
1129 #endif
|
|
1130 #if defined (BSD) || defined (__sun__)
|
|
1131 # include <sys/signal.h>
|
|
1132 #endif
|
|
1133 #if defined (__hpux__)
|
|
1134 # include <sys/stropts.h>
|
|
1135 #endif
|
|
1136
|
|
1137 #define CDISABLE _POSIX_VDISABLE
|
|
1138
|
|
1139 /* On HP-UX and Sun system, there is a bzero function but with a different
|
|
1140 signature. Use memset instead */
|
|
1141 #if defined (__hpux__) || defined (__sun__) || defined (_AIX)
|
|
1142 # define bzero(s,n) memset (s,0,n)
|
|
1143 #endif
|
|
1144
|
|
1145 /* POSIX does not specify how to open the master side of a terminal.Several
|
|
1146 methods are available (system specific):
|
|
1147 1- using a cloning device (USE_CLONE_DEVICE)
|
|
1148 2- getpt (USE_GETPT)
|
|
1149 3- openpty (USE_OPENPTY)
|
|
1150
|
|
1151 When using the cloning device method, the macro USE_CLONE_DEVICE should
|
|
1152 contains a full path to the adequate device.
|
|
1153
|
|
1154 When a new system is about to be supported, one of the previous macro should
|
|
1155 be set otherwise allocate_pty_desc will return an error
|
|
1156 */
|
|
1157
|
|
1158 /* Configurable part */
|
|
1159 #if defined (__APPLE__) || defined (BSD)
|
|
1160 #define USE_OPENPTY
|
|
1161 #elif defined (__linux__)
|
|
1162 #define USE_GETPT
|
|
1163 #elif defined (__sun__)
|
|
1164 #define USE_CLONE_DEVICE "/dev/ptmx"
|
|
1165 #elif defined (_AIX)
|
|
1166 #define USE_CLONE_DEVICE "/dev/ptc"
|
|
1167 #elif defined (__hpux__)
|
|
1168 /* On HP-UX we use the streamed version. Using the non streamed version is not
|
|
1169 recommanded (through "/dev/ptym/clone"). Indeed it seems that there are
|
|
1170 issues to detect process terminations. */
|
|
1171 #define USE_CLONE_DEVICE "/dev/ptmx"
|
|
1172 #endif
|
|
1173
|
|
1174 /* structure that holds information about the terminal used and the process
|
|
1175 connected on the slave side */
|
|
1176 typedef struct pty_desc_struct {
|
|
1177 int master_fd; /* fd of the master side if the terminal */
|
|
1178 int slave_fd; /* fd of the slave side */
|
|
1179 char slave_name[32]; /* filename of the slave side */
|
|
1180 int child_pid; /* PID of the child process connected to the slave side
|
|
1181 of the terminal */
|
|
1182 } pty_desc;
|
|
1183
|
|
1184 /* allocate_pty_desc - allocate a pseudo terminal
|
|
1185 *
|
|
1186 * PARAMETERS
|
|
1187 * out desc returned pointer to a pty_desc structure containing information
|
|
1188 * about the opened pseudo terminal
|
|
1189 * RETURN VALUE
|
|
1190 * -1 if failed
|
|
1191 * 0 if ok
|
|
1192 * COMMENTS
|
|
1193 * If the function is successful we should have at least the master side fd
|
|
1194 * and the slave side filename. On some system, the slave side will also be
|
|
1195 * opened. If this is not the case the slave side will be open once we are in
|
|
1196 * the child process (note that opening the slave side at this stage will
|
|
1197 * failed...).
|
|
1198 */
|
|
1199
|
|
1200 extern char* ptsname (int);
|
|
1201
|
|
1202 static int
|
|
1203 allocate_pty_desc (pty_desc **desc) {
|
|
1204
|
|
1205 pty_desc *result;
|
|
1206 int status = 0;
|
|
1207 int slave_fd = -1;
|
|
1208 int master_fd = -1;
|
|
1209 char *slave_name = NULL;
|
|
1210
|
|
1211 #ifdef USE_GETPT
|
|
1212 master_fd = getpt ();
|
|
1213 #elif defined (USE_OPENPTY)
|
|
1214 status = openpty (&master_fd, &slave_fd, NULL, NULL, NULL);
|
|
1215 #elif defined (USE_CLONE_DEVICE)
|
|
1216 master_fd = open (USE_CLONE_DEVICE, O_RDWR | O_NONBLOCK, 0);
|
|
1217 #else
|
|
1218 printf ("[error]: terminal support is not configured\n");
|
|
1219 return -1;
|
|
1220 #endif
|
|
1221
|
|
1222 /* at this stage we should have the master side fd and status should be 0 */
|
|
1223 if (status != 0 || master_fd < 0)
|
|
1224 {
|
|
1225 /* If this is not the case close all opened files and return -1 */
|
|
1226 printf ("[error]: cannot allocate master side of the pty\n");
|
|
1227 if (master_fd >= 0) close (master_fd);
|
|
1228 if (slave_fd >= 0) close (slave_fd);
|
|
1229 *desc = NULL;
|
|
1230 return -1;
|
|
1231 }
|
|
1232
|
|
1233 /* retrieve the file name of the slave side if necessary */
|
|
1234 if (slave_name == NULL) slave_name = (char *) ptsname (master_fd);
|
|
1235
|
|
1236 /* Now we should have slave file name */
|
|
1237 if (slave_name == NULL)
|
|
1238 {
|
|
1239 /* If not the case close any opened file and return - 1 */
|
|
1240 printf ("[error]: cannot allocate slave side of the pty\n");
|
|
1241 if (master_fd >= 0) close (master_fd);
|
|
1242 if (slave_fd >= 0) close (slave_fd);
|
|
1243 *desc = NULL;
|
|
1244 return -1;
|
|
1245 }
|
|
1246
|
|
1247 #if !defined(__rtems__)
|
|
1248 /* grant access to the slave side */
|
|
1249 grantpt (master_fd);
|
|
1250 /* unlock the terminal */
|
|
1251 unlockpt (master_fd);
|
|
1252 #endif
|
|
1253
|
|
1254 /* set desc and return 0 */
|
|
1255 result = malloc (sizeof (pty_desc));
|
|
1256 result->master_fd = master_fd;
|
|
1257 result->slave_fd = slave_fd;
|
|
1258 /* the string returned by ptsname or _getpty is a static allocated string. So
|
|
1259 we should make a copy */
|
|
1260 strncpy (result->slave_name, slave_name, sizeof (result->slave_name));
|
|
1261 result->slave_name[sizeof (result->slave_name) - 1] = '\0';
|
|
1262 result->child_pid = -1;
|
|
1263 *desc=result;
|
|
1264 return 0;
|
|
1265 }
|
|
1266
|
|
1267 /* some utility macro that make the code of child_setup_tty easier to read */
|
|
1268 #define __enable(a, b) ((a) |= (b))
|
|
1269 #define __disable(a, b) ((a) &= ~(b))
|
|
1270
|
|
1271 /* some properties do not exist on all systems. Set their value to 0 in that
|
|
1272 case */
|
|
1273 #ifndef IUCLC
|
|
1274 #define IUCLC 0
|
|
1275 #endif
|
|
1276 #ifndef OLCUC
|
|
1277 #define OLCUC 0
|
|
1278 #endif
|
|
1279 #ifndef NLDLY
|
|
1280 #define NLDLY 0
|
|
1281 #define CRDLY 0
|
|
1282 #define TABDLY 0
|
|
1283 #define BSDLY 0
|
|
1284 #define VTDLY 0
|
|
1285 #define FFDLY 0
|
|
1286 #endif
|
|
1287
|
|
1288 /* child_setup_tty - set terminal properties
|
|
1289 *
|
|
1290 * PARAMETERS
|
|
1291 * file descriptor of the slave side of the terminal
|
|
1292 *
|
|
1293 * RETURN VALUE
|
|
1294 * 0 if success, any other value if failed.
|
|
1295 *
|
|
1296 * COMMENTS
|
|
1297 * None
|
|
1298 */
|
|
1299 static int
|
|
1300 child_setup_tty (int fd)
|
|
1301 {
|
|
1302 struct termios s;
|
|
1303 int status;
|
|
1304
|
|
1305 /* ensure that s is filled with 0 */
|
|
1306 bzero (&s, sizeof (s));
|
|
1307
|
|
1308 /* Get the current terminal settings */
|
|
1309 status = tcgetattr (fd, &s);
|
|
1310 if (status != 0) return -1;
|
|
1311
|
|
1312 /* Adjust input modes */
|
|
1313 __disable (s.c_iflag, IUCLC); /* don't transform to lower case */
|
|
1314 __disable (s.c_iflag, ISTRIP); /* don't delete 8th bit */
|
|
1315
|
|
1316 /* Adjust output modes */
|
|
1317 __enable (s.c_oflag, OPOST); /* enable postprocessing */
|
|
1318 __disable (s.c_oflag, ONLCR); /* don't map LF to CR-LF */
|
|
1319 __disable (s.c_oflag, NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
|
|
1320 /* disable delays */
|
|
1321 __disable (s.c_oflag, OLCUC); /* don't transform to upper case */
|
|
1322
|
|
1323 /* Adjust control modes */
|
|
1324 s.c_cflag = (s.c_cflag & ~CSIZE) | CS8; /* Don't strip 8th bit */
|
|
1325
|
|
1326 /* Adjust local modes */
|
|
1327 __disable (s.c_lflag, ECHO); /* disable echo */
|
|
1328 __enable (s.c_lflag, ISIG); /* enable signals */
|
|
1329 __enable (s.c_lflag, ICANON); /* erase/kill/eof processing */
|
|
1330
|
|
1331 /* Adjust control characters */
|
|
1332 /* IMPORTANT: we need to ensure that Ctrl-C will trigger an interrupt signal
|
|
1333 otherwise send_signal_via_characters will fail */
|
|
1334 s.c_cc[VEOF] = 04; /* insure that EOF is Control-D */
|
|
1335 s.c_cc[VERASE] = CDISABLE; /* disable erase processing */
|
|
1336 s.c_cc[VKILL] = CDISABLE; /* disable kill processing */
|
|
1337 s.c_cc[VQUIT] = 28; /* Control-\ */
|
|
1338 s.c_cc[VINTR] = 03; /* Control-C */
|
|
1339 s.c_cc[VEOL] = CDISABLE;
|
|
1340 s.c_cc[VSUSP] = 26; /* Control-Z */
|
|
1341
|
|
1342 /* push our changes */
|
|
1343 status = tcsetattr (fd, TCSADRAIN, &s);
|
|
1344 return status;
|
|
1345 }
|
|
1346
|
|
1347 /* __gnat_setup_communication - interface to the external world. Should be
|
|
1348 * called before forking. On Unixes this function only call allocate_pty_desc.
|
|
1349 * The Windows implementation (in different part of this file) is very
|
|
1350 * different.
|
|
1351 *
|
|
1352 * PARAMETERS
|
|
1353 * out desc returned pointer to a pty_desc structure
|
|
1354 * RETURN VALUE
|
|
1355 * 0 if success, -1 otherwise
|
|
1356 */
|
|
1357 int __gnat_setup_communication (pty_desc** desc) {
|
|
1358 return allocate_pty_desc (desc);
|
|
1359 }
|
|
1360
|
|
1361 /* __gnat_setup_parent_communication - interface to the external world. Should
|
|
1362 * be called after forking in the parent process
|
|
1363 *
|
|
1364 * PARAMETERS
|
|
1365 * out in_fd
|
|
1366 out out_fd
|
|
1367 out err_fd fds corresponding to the parent side of the
|
|
1368 terminal
|
|
1369 in pid_out child process pid
|
|
1370 * RETRUN VALUE
|
|
1371 * 0
|
|
1372 */
|
|
1373 void
|
|
1374 __gnat_setup_parent_communication
|
|
1375 (pty_desc *desc,
|
|
1376 int* in_fd, /* input */
|
|
1377 int* out_fd, /* output */
|
|
1378 int* err_fd, /* error */
|
|
1379 int* pid_out)
|
|
1380 {
|
|
1381
|
|
1382 *in_fd = desc->master_fd;
|
|
1383 *out_fd= desc->master_fd;
|
|
1384 *err_fd= desc->master_fd;
|
|
1385 desc->child_pid = *pid_out;
|
|
1386 }
|
|
1387
|
|
1388 /* __gnat_setup_winsize - Sets up the size of the terminal
|
|
1389 * This lets the process know the size of the terminal
|
|
1390 */
|
|
1391
|
|
1392 void __gnat_setup_winsize (pty_desc *desc, int rows, int columns) {
|
|
1393 #ifdef TIOCGWINSZ
|
|
1394 struct winsize s;
|
|
1395 s.ws_row = (unsigned short)rows;
|
|
1396 s.ws_col = (unsigned short)columns;
|
|
1397 s.ws_xpixel = 0;
|
|
1398 s.ws_ypixel = 0;
|
|
1399 ioctl (desc->master_fd, TIOCSWINSZ, &s);
|
|
1400 #ifdef SIGWINCH
|
|
1401 if (desc->child_pid > 0) {
|
|
1402 /* Let the process know about the change in size */
|
|
1403 kill (desc->child_pid, SIGWINCH);
|
|
1404 }
|
|
1405 #endif
|
|
1406 #endif
|
|
1407 }
|
|
1408
|
|
1409 /* __gnat_setup_child_communication - interface to external world. Should be
|
|
1410 * called after forking in the child process. On Unixes, this function
|
|
1411 * first adjust the line setting, set standard output, input and error and
|
|
1412 * then spawn the program.
|
|
1413 *
|
|
1414 * PARAMETERS
|
|
1415 * desc a pty_desc structure containing the pty parameters
|
|
1416 * new_argv argv of the program to be spawned
|
|
1417 * RETURN VALUE
|
|
1418 * this function should not return
|
|
1419 */
|
|
1420 int
|
|
1421 __gnat_setup_child_communication
|
|
1422 (pty_desc *desc,
|
|
1423 char **new_argv,
|
131
|
1424 int Use_Pipes ATTRIBUTE_UNUSED)
|
111
|
1425 {
|
|
1426 int status;
|
|
1427 int pid = getpid ();
|
|
1428
|
|
1429 setsid ();
|
|
1430
|
|
1431 /* open the slave side of the terminal if necessary */
|
|
1432 if (desc->slave_fd == -1)
|
|
1433 #if defined (_AIX)
|
|
1434 /* On AIX, if the slave process is not opened with O_NDELAY or O_NONBLOCK
|
|
1435 then we might have some processes hanging on I/O system calls. Not sure
|
|
1436 we can do that for all platforms so do it only on AIX for the moment.
|
|
1437 On AIX O_NONBLOCK and O_NDELAY have slightly different meanings. When
|
|
1438 reading on the slave fd, in case there is no data available, if O_NDELAY
|
|
1439 is set then 0 is returned. If O_NON_BLOCK is -1 is returned. It seems
|
|
1440 that interactive programs such as GDB prefer the O_NDELAY behavior.
|
|
1441 We chose O_NONBLOCK because it allows us to make the distinction
|
|
1442 between a true EOF and an EOF returned because there is no data
|
|
1443 available to be read. */
|
|
1444 desc->slave_fd = open (desc->slave_name, O_RDWR | O_NONBLOCK, 0);
|
|
1445 #else
|
|
1446 desc->slave_fd = open (desc->slave_name, O_RDWR, 0);
|
|
1447 #endif
|
|
1448
|
|
1449 #if defined (__sun__) || defined (__hpux__)
|
|
1450 /* On systems such as Solaris we are using stream. We need to push the right
|
|
1451 "modules" in order to get the expected terminal behaviors. Otherwise
|
|
1452 functionalities such as termios are not available. */
|
|
1453 ioctl (desc->slave_fd, I_PUSH, "ptem");
|
|
1454 ioctl (desc->slave_fd, I_PUSH, "ldterm");
|
|
1455 ioctl (desc->slave_fd, I_PUSH, "ttcompat");
|
|
1456 #endif
|
|
1457
|
|
1458 #ifdef TIOCSCTTY
|
|
1459 /* make the tty the controlling terminal */
|
|
1460 if ((status = ioctl (desc->slave_fd, TIOCSCTTY, 0)) == -1)
|
131
|
1461 _exit (1);
|
111
|
1462 #endif
|
|
1463
|
|
1464 /* adjust tty settings */
|
|
1465 child_setup_tty (desc->slave_fd);
|
|
1466 __gnat_setup_winsize (desc, 24, 80); /* To prevent errors in some shells */
|
|
1467
|
|
1468 /* stdin, stdout and stderr should be now our tty */
|
|
1469 dup2 (desc->slave_fd, 0);
|
|
1470 dup2 (desc->slave_fd, 1);
|
|
1471 dup2 (desc->slave_fd, 2);
|
|
1472 if (desc->slave_fd > 2) close (desc->slave_fd);
|
|
1473
|
|
1474 /* adjust process group settings */
|
|
1475 /* ignore failures of the following two commands as the context might not
|
|
1476 * allow making those changes. */
|
|
1477 setpgid (pid, pid);
|
|
1478 tcsetpgrp (0, pid);
|
|
1479
|
|
1480 /* launch the program */
|
|
1481 execvp (new_argv[0], new_argv);
|
|
1482
|
131
|
1483 _exit (1);
|
111
|
1484 }
|
|
1485
|
|
1486 /* send_signal_via_characters - Send a characters that will trigger a signal
|
|
1487 * in the child process.
|
|
1488 *
|
|
1489 * PARAMETERS
|
|
1490 * desc a pty_desc structure containing terminal information
|
|
1491 * int a signal number
|
|
1492 * RETURN VALUE
|
|
1493 * None
|
|
1494 */
|
|
1495 static void
|
|
1496 send_signal_via_characters
|
|
1497 (pty_desc *desc,
|
|
1498 int signal_number)
|
|
1499 {
|
|
1500 char ctrl_c = 03;
|
|
1501 char ctrl_backslash = 28;
|
|
1502 char ctrl_Z = 26;
|
|
1503
|
|
1504 switch (signal_number)
|
|
1505 {
|
|
1506 case SIGINT:
|
|
1507 write (desc->master_fd, &ctrl_c, 1); return;
|
|
1508 case SIGQUIT:
|
|
1509 write (desc->master_fd, &ctrl_backslash, 1); return;
|
|
1510 case SIGTSTP:
|
|
1511 write (desc->master_fd, &ctrl_Z, 1); return;
|
|
1512 }
|
|
1513 }
|
|
1514
|
|
1515 /* __gnat_interrupt_process - interrupt the child process
|
|
1516 *
|
|
1517 * PARAMETERS
|
|
1518 * desc a pty_desc structure
|
|
1519 */
|
|
1520 int
|
|
1521 __gnat_interrupt_process (pty_desc *desc)
|
|
1522 {
|
|
1523 send_signal_via_characters (desc, SIGINT);
|
|
1524 return 0;
|
|
1525 }
|
|
1526
|
|
1527 /* __gnat_interrupt_pid - interrupt a process group
|
|
1528 *
|
|
1529 * PARAMETERS
|
|
1530 * pid pid of the process to interrupt
|
|
1531 */
|
|
1532 int
|
|
1533 __gnat_interrupt_pid (int pid)
|
|
1534 {
|
|
1535 kill (-pid, SIGINT);
|
|
1536 return 0;
|
|
1537 }
|
|
1538
|
|
1539 /* __gnat_terminate_process - kill a child process
|
|
1540 *
|
|
1541 * PARAMETERS
|
|
1542 * desc pty_desc structure
|
|
1543 */
|
|
1544 int __gnat_terminate_process (pty_desc *desc)
|
|
1545 {
|
|
1546 return kill (desc->child_pid, SIGKILL);
|
|
1547 }
|
|
1548
|
|
1549 /* __gnat_terminate_pid - kill a process
|
|
1550 *
|
|
1551 * PARAMETERS
|
|
1552 * pid unix process id
|
|
1553 */
|
|
1554 int
|
|
1555 __gnat_terminate_pid (int pid)
|
|
1556 {
|
|
1557 return kill (pid, SIGKILL);
|
|
1558 }
|
|
1559
|
|
1560 /* __gnat_tty_waitpid - wait for the child process to die
|
|
1561 *
|
|
1562 * PARAMETERS
|
|
1563 * desc pty_desc structure
|
|
1564 * RETURN VALUE
|
|
1565 * exit status of the child process
|
|
1566 */
|
|
1567 int
|
145
|
1568 __gnat_tty_waitpid (pty_desc *desc, int blocking)
|
111
|
1569 {
|
145
|
1570 int status = -1;
|
|
1571 int options = 0;
|
|
1572
|
|
1573 if (blocking) {
|
|
1574 options = 0;
|
|
1575 } else {
|
|
1576 options = WNOHANG;
|
|
1577 }
|
|
1578 waitpid (desc->child_pid, &status, options);
|
|
1579 if WIFEXITED (status) {
|
|
1580 status = WEXITSTATUS (status);
|
|
1581 }
|
|
1582 return status;
|
111
|
1583 }
|
|
1584
|
|
1585 /* __gnat_tty_supported - Are tty supported ?
|
|
1586 *
|
|
1587 * RETURN VALUE
|
|
1588 * always 1 on Unix systems
|
|
1589 */
|
|
1590 int
|
|
1591 __gnat_tty_supported (void)
|
|
1592 {
|
|
1593 return 1;
|
|
1594 }
|
|
1595
|
|
1596 /* __gnat_free_process - free a pty_desc structure
|
|
1597 *
|
|
1598 * PARAMETERS
|
|
1599 * in out desc: a pty desc structure
|
|
1600 */
|
|
1601 void
|
|
1602 __gnat_free_process (pty_desc** desc)
|
|
1603 {
|
|
1604 free (*desc);
|
|
1605 *desc = NULL;
|
|
1606 }
|
|
1607
|
|
1608 /* __gnat_send_header - dummy function. this interface is only used on Windows */
|
|
1609 void
|
131
|
1610 __gnat_send_header (pty_desc* desc ATTRIBUTE_UNUSED,
|
|
1611 char header[5] ATTRIBUTE_UNUSED,
|
|
1612 int size ATTRIBUTE_UNUSED,
|
|
1613 int *ret ATTRIBUTE_UNUSED)
|
111
|
1614 {
|
|
1615 *ret = 0;
|
|
1616 }
|
|
1617
|
|
1618 /* __gnat_reset_tty - reset line setting
|
|
1619 *
|
|
1620 * PARAMETERS
|
|
1621 * desc: a pty_desc structure
|
|
1622 */
|
|
1623 void
|
|
1624 __gnat_reset_tty (pty_desc* desc)
|
|
1625 {
|
|
1626 child_setup_tty (desc->master_fd);
|
|
1627 }
|
|
1628
|
|
1629 /* __gnat_new_tty - allocate a new terminal
|
|
1630 *
|
|
1631 * RETURN VALUE
|
|
1632 * a pty_desc structure
|
|
1633 */
|
|
1634 pty_desc *
|
|
1635 __gnat_new_tty (void)
|
|
1636 {
|
|
1637 int status;
|
|
1638 pty_desc* desc = NULL;
|
|
1639 if ((status = allocate_pty_desc (&desc)))
|
|
1640 child_setup_tty (desc->master_fd);
|
|
1641 return desc;
|
|
1642 }
|
|
1643
|
|
1644 /* __gnat_close_tty - close a terminal
|
|
1645 *
|
|
1646 * PARAMETERS
|
|
1647 * desc a pty_desc strucure
|
|
1648 */
|
|
1649 void __gnat_close_tty (pty_desc* desc)
|
|
1650 {
|
145
|
1651 if (desc->master_fd >= 0) { close (desc->master_fd); desc->master_fd = -1; }
|
|
1652 if (desc->slave_fd >= 0) { close (desc->slave_fd); desc->slave_fd = -1; }
|
111
|
1653 }
|
|
1654
|
|
1655 /* __gnat_tty_name - return slave side device name
|
|
1656 *
|
|
1657 * PARAMETERS
|
|
1658 * desc a pty_desc strucure
|
|
1659 * RETURN VALUE
|
|
1660 * a string
|
|
1661 */
|
|
1662 char *
|
|
1663 __gnat_tty_name (pty_desc* desc)
|
|
1664 {
|
|
1665 return desc->slave_name;
|
|
1666 }
|
|
1667
|
|
1668 /* __gnat_tty_name - return master side fd
|
|
1669 *
|
|
1670 * PARAMETERS
|
|
1671 * desc a pty_desc strucure
|
|
1672 * RETURN VALUE
|
|
1673 * a fd
|
|
1674 */
|
|
1675 int
|
|
1676 __gnat_tty_fd (pty_desc* desc)
|
|
1677 {
|
|
1678 return desc->master_fd;
|
|
1679 }
|
|
1680
|
|
1681 #endif /* WIN32 */
|