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