1 /****************************************************************************
2  *                                                                          *
3  *                         GNAT RUN-TIME COMPONENTS                         *
4  *                                                                          *
5  *                            T E R M I N A L S                             *
6  *                                                                          *
7  *                          C Implementation File                           *
8  *                                                                          *
9  *                     Copyright (C) 2008-2015, AdaCore                     *
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   || defined (__ANDROID__) || defined (__PikeOS__)
36 
37 #define ATTRIBUTE_UNUSED __attribute__((unused))
38 
39 void *
__gnat_new_tty(void)40 __gnat_new_tty (void)
41 {
42   return (void*)0;
43 }
44 
45 char *
__gnat_tty_name(void * t ATTRIBUTE_UNUSED)46 __gnat_tty_name (void* t ATTRIBUTE_UNUSED)
47 {
48   return (char*)0;
49 }
50 
51 int
__gnat_interrupt_pid(int pid ATTRIBUTE_UNUSED)52 __gnat_interrupt_pid (int pid ATTRIBUTE_UNUSED)
53 {
54   return -1;
55 }
56 
57 int
__gnat_interrupt_process(void * desc ATTRIBUTE_UNUSED)58 __gnat_interrupt_process (void* desc ATTRIBUTE_UNUSED)
59 {
60   return -1;
61 }
62 
63 int
__gnat_setup_communication(void ** desc ATTRIBUTE_UNUSED)64 __gnat_setup_communication (void** desc ATTRIBUTE_UNUSED)
65 {
66   return -1;
67 }
68 
69 void
__gnat_setup_parent_communication(void * d ATTRIBUTE_UNUSED,int * i ATTRIBUTE_UNUSED,int * o ATTRIBUTE_UNUSED,int * e ATTRIBUTE_UNUSED,int * p ATTRIBUTE_UNUSED)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
__gnat_setup_child_communication(void * d ATTRIBUTE_UNUSED,char ** n ATTRIBUTE_UNUSED,int u ATTRIBUTE_UNUSED)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
__gnat_terminate_process(void * desc ATTRIBUTE_UNUSED)87 __gnat_terminate_process (void *desc ATTRIBUTE_UNUSED)
88 {
89   return -1;
90 }
91 
92 int
__gnat_tty_fd(void * t ATTRIBUTE_UNUSED)93 __gnat_tty_fd (void* t ATTRIBUTE_UNUSED)
94 {
95   return -1;
96 }
97 
98 int
__gnat_tty_supported(void)99 __gnat_tty_supported (void)
100 {
101   return 0;
102 }
103 
104 int
__gnat_tty_waitpid(void * desc ATTRIBUTE_UNUSED)105 __gnat_tty_waitpid (void *desc ATTRIBUTE_UNUSED)
106 {
107   return 1;
108 }
109 
110 void
__gnat_close_tty(void * t ATTRIBUTE_UNUSED)111 __gnat_close_tty (void* t ATTRIBUTE_UNUSED)
112 {
113 }
114 
115 void
__gnat_free_process(void ** process ATTRIBUTE_UNUSED)116 __gnat_free_process (void** process ATTRIBUTE_UNUSED)
117 {
118 }
119 
120 void
__gnat_reset_tty(void * t ATTRIBUTE_UNUSED)121 __gnat_reset_tty (void* t ATTRIBUTE_UNUSED)
122 {
123 }
124 
125 void
__gnat_send_header(void * d ATTRIBUTE_UNUSED,char h[5]ATTRIBUTE_UNUSED,int s ATTRIBUTE_UNUSED,int * r ATTRIBUTE_UNUSED)126 __gnat_send_header (void* d ATTRIBUTE_UNUSED,
127 		    char h[5] ATTRIBUTE_UNUSED,
128 		    int s ATTRIBUTE_UNUSED,
129 		    int *r ATTRIBUTE_UNUSED)
130 {
131 }
132 
133 void
__gnat_setup_winsize(void * desc ATTRIBUTE_UNUSED,int rows ATTRIBUTE_UNUSED,int columns ATTRIBUTE_UNUSED)134 __gnat_setup_winsize (void *desc ATTRIBUTE_UNUSED,
135 		      int rows ATTRIBUTE_UNUSED,
136 		      int columns ATTRIBUTE_UNUSED)
137 {
138 }
139 
140 /* For Windows platforms. */
141 
142 #elif defined(_WIN32)
143 
144 #include <errno.h>
145 #include <stdio.h>
146 #include <stdlib.h>
147 
148 #include <windows.h>
149 
150 #define MAXPATHLEN 1024
151 
152 #define NILP(x) ((x) == 0)
153 #define Qnil 0
154 #define report_file_error(x, y) fprintf (stderr, "Error: %s\n", x);
155 #define INTEGERP(x) 1
156 #define XINT(x) x
157 
158 struct TTY_Process {
159   int pid;           /* Number of this process */
160   PROCESS_INFORMATION procinfo;
161   HANDLE w_infd, w_outfd;
162   HANDLE w_forkin, w_forkout;
163   BOOL usePipe;
164 };
165 
166 /* Control whether create_child cause the process to inherit GPS'
167    error mode setting.  The default is 1, to minimize the possibility of
168    subprocesses blocking when accessing unmounted drives.  */
169 static int Vw32_start_process_inherit_error_mode = 1;
170 
171 /* Control whether spawnve quotes arguments as necessary to ensure
172    correct parsing by child process.  Because not all uses of spawnve
173    are careful about constructing argv arrays, we make this behavior
174    conditional (off by default, since a similar operation is already done
175    in g-expect.adb by calling Normalize_Argument). */
176 static int Vw32_quote_process_args = 0;
177 
178 static DWORD AbsoluteSeek(HANDLE, DWORD);
179 static VOID  ReadBytes(HANDLE, LPVOID, DWORD);
180 
181 #define XFER_BUFFER_SIZE 2048
182 
183 /* This tell if the executable we're about to launch uses a GUI interface. */
184 /* if we can't determine it, we will return true */
185 static int
is_gui_app(char * exe)186 is_gui_app (char *exe)
187 {
188   HANDLE hImage;
189 
190   DWORD  bytes;
191   DWORD  iSection;
192   DWORD  SectionOffset;
193   DWORD  CoffHeaderOffset;
194   DWORD  MoreDosHeader[16];
195   CHAR   *file;
196   size_t nlen;
197 
198   ULONG  ntSignature;
199 
200   IMAGE_DOS_HEADER      image_dos_header;
201   IMAGE_FILE_HEADER     image_file_header;
202   IMAGE_OPTIONAL_HEADER image_optional_header;
203   IMAGE_SECTION_HEADER  image_section_header;
204 
205   /*
206    *  Open the reference file.
207   */
208   nlen = strlen (exe);
209   file = exe;
210   if (nlen > 2) {
211     if (exe[0] == '"') {
212       /* remove quotes */
213       nlen -= 2;
214       file = malloc ((nlen + 1) * sizeof (char));
215       memcpy (file, &exe[1], nlen);
216       file [nlen] = '\0';
217     }
218   }
219   hImage = CreateFile(file,
220                       GENERIC_READ,
221                       FILE_SHARE_READ,
222                       NULL,
223                       OPEN_EXISTING,
224                       FILE_ATTRIBUTE_NORMAL,
225                       NULL);
226 
227   if (file != exe) {
228     free (file);
229   }
230 
231   if (INVALID_HANDLE_VALUE == hImage)
232     {
233       report_file_error ("Could not open exe: ", Qnil);
234       report_file_error (exe, Qnil);
235       report_file_error ("\n", Qnil);
236       CloseHandle (hImage);
237       return -1;
238     }
239 
240   /*
241    *  Read the MS-DOS image header.
242    */
243   ReadBytes(hImage, &image_dos_header, sizeof(IMAGE_DOS_HEADER));
244 
245   if (IMAGE_DOS_SIGNATURE != image_dos_header.e_magic)
246     {
247       report_file_error("Sorry, I do not understand this file.\n", Qnil);
248       CloseHandle (hImage);
249       return -1;
250     }
251 
252   /*
253    *  Read more MS-DOS header.       */
254   ReadBytes(hImage, MoreDosHeader, sizeof(MoreDosHeader));
255    /*
256    *  Get actual COFF header.
257    */
258   CoffHeaderOffset = AbsoluteSeek(hImage, image_dos_header.e_lfanew) +
259                      sizeof(ULONG);
260   if (CoffHeaderOffset < 0) {
261     CloseHandle (hImage);
262     return -1;
263   }
264 
265   ReadBytes (hImage, &ntSignature, sizeof(ULONG));
266 
267   if (IMAGE_NT_SIGNATURE != ntSignature)
268     {
269       report_file_error ("Missing NT signature. Unknown file type.\n", Qnil);
270       CloseHandle (hImage);
271       return -1;
272     }
273 
274   SectionOffset = CoffHeaderOffset + IMAGE_SIZEOF_FILE_HEADER +
275     IMAGE_SIZEOF_NT_OPTIONAL_HEADER;
276 
277   ReadBytes(hImage, &image_file_header, IMAGE_SIZEOF_FILE_HEADER);
278 
279   /*
280    *  Read optional header.
281    */
282   ReadBytes(hImage,
283             &image_optional_header,
284             IMAGE_SIZEOF_NT_OPTIONAL_HEADER);
285 
286   CloseHandle (hImage);
287 
288   switch (image_optional_header.Subsystem)
289     {
290     case IMAGE_SUBSYSTEM_UNKNOWN:
291         return 1;
292         break;
293 
294     case IMAGE_SUBSYSTEM_NATIVE:
295         return 1;
296         break;
297 
298     case IMAGE_SUBSYSTEM_WINDOWS_GUI:
299         return 1;
300         break;
301 
302     case IMAGE_SUBSYSTEM_WINDOWS_CUI:
303         return 0;
304         break;
305 
306     case IMAGE_SUBSYSTEM_OS2_CUI:
307         return 0;
308         break;
309 
310     case IMAGE_SUBSYSTEM_POSIX_CUI:
311         return 0;
312         break;
313 
314     default:
315         /* Unknown, return GUI app to be preservative: if yes, it will be
316            correctly launched, if no, it will be launched, and a console will
317            be also displayed, which is not a big deal */
318         return 1;
319         break;
320     }
321 
322 }
323 
324 static DWORD
AbsoluteSeek(HANDLE hFile,DWORD offset)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
ReadBytes(HANDLE hFile,LPVOID buffer,DWORD size)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
nt_spawnve(char * exe,char ** argv,char * env,struct TTY_Process * process)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
__gnat_send_header(struct TTY_Process * p,char header[5],int size,int * ret)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
__gnat_setup_communication(struct TTY_Process ** process_out)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
__gnat_setup_child_communication(struct TTY_Process * process,char ** argv,int Use_Pipes)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
__gnat_setup_parent_communication(struct TTY_Process * process,int * in,int * out,int * err,int * pid)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
cache_system_info(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
find_child_console(HWND hwnd,child_process * cp)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
__gnat_interrupt_process(struct TTY_Process * p)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
__gnat_interrupt_pid(int pid)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
__gnat_terminate_process(struct TTY_Process * p)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 /* wait for process pid to terminate and return the process status. This
973    implementation is different from the adaint.c one for Windows as it uses
974    the Win32 API instead of the C one. */
975 
976 int
__gnat_tty_waitpid(struct TTY_Process * p)977 __gnat_tty_waitpid (struct TTY_Process* p)
978 {
979   DWORD exitcode;
980   DWORD res;
981   HANDLE proc_hand = p->procinfo.hProcess;
982 
983   res = WaitForSingleObject (proc_hand, 0);
984   GetExitCodeProcess (proc_hand, &exitcode);
985 
986   CloseHandle (p->procinfo.hThread);
987   CloseHandle (p->procinfo.hProcess);
988 
989   /* No need to close the handles: they were closed on the ada side */
990 
991   return (int) exitcode;
992 }
993 
994 /********************************
995  **  __gnat_free_process ()
996  ********************************/
997 
998 void
__gnat_free_process(struct TTY_Process ** process)999 __gnat_free_process (struct TTY_Process** process)
1000 {
1001   free (*process);
1002   *process = NULL;
1003 }
1004 
1005 /* TTY handling */
1006 
1007 typedef struct {
1008   int tty_fd;        /* descriptor for the tty */
1009   char tty_name[24]; /* Name of TTY device */
1010 } TTY_Handle;
1011 
1012 int
__gnat_tty_supported(void)1013 __gnat_tty_supported (void)
1014 {
1015   return 0;
1016 }
1017 
1018 /* Return the tty name associated with p */
1019 
1020 char *
__gnat_tty_name(TTY_Handle * t)1021 __gnat_tty_name (TTY_Handle* t)
1022 {
1023   return t->tty_name;
1024 }
1025 
1026 int
__gnat_tty_fd(TTY_Handle * t)1027 __gnat_tty_fd (TTY_Handle* t)
1028 {
1029   return t->tty_fd;
1030 }
1031 
1032 TTY_Handle*
__gnat_new_tty(void)1033 __gnat_new_tty (void)
1034 {
1035   return (TTY_Handle*)0;
1036 }
1037 
1038 void
__gnat_reset_tty(TTY_Handle * t)1039 __gnat_reset_tty (TTY_Handle* t)
1040 {
1041   return;
1042 }
1043 
1044 void
__gnat_close_tty(TTY_Handle * t)1045 __gnat_close_tty (TTY_Handle* t)
1046 {
1047   free (t);
1048 }
1049 
1050 void
__gnat_setup_winsize(void * desc,int rows,int columns)1051 __gnat_setup_winsize (void *desc, int rows, int columns)
1052 {
1053 }
1054 
1055 #else /* defined(_WIN32, implementatin for all UNIXes */
1056 
1057 /* First defined some macro to identify easily some systems */
1058 #if defined (__FreeBSD__) \
1059  || defined (__OpenBSD__) \
1060  || defined (__NetBSD__)  \
1061  || defined (__DragonFly__)
1062 #   define BSD
1063 #endif
1064 
1065 /* Include every system header we need */
1066 #define _GNU_SOURCE
1067 #include <errno.h>
1068 #include <stdio.h>
1069 #include <stdlib.h>
1070 
1071 /* On some system termio is either absent or including it will disable termios
1072    (HP-UX) */
1073 #if !defined (__hpux__) && !defined (BSD) && !defined (__APPLE__) \
1074   && !defined (__rtems__)
1075 #   include <termio.h>
1076 #endif
1077 
1078 #include <sys/ioctl.h>
1079 #include <termios.h>
1080 #include <fcntl.h>
1081 #include <string.h>
1082 #include <sys/stat.h>
1083 #include <sys/types.h>
1084 #include <sys/wait.h>
1085 #include <unistd.h>
1086 #if defined (__sun__)
1087 #   include <sys/stropts.h>
1088 #endif
1089 #if defined (BSD) || defined (__sun__)
1090 #   include <sys/signal.h>
1091 #endif
1092 #if defined (__hpux__)
1093 #   include <sys/termio.h>
1094 #   include <sys/stropts.h>
1095 #endif
1096 
1097 #define CDISABLE _POSIX_VDISABLE
1098 
1099 /* On HP-UX and Sun system, there is a bzero function but with a different
1100    signature. Use memset instead */
1101 #if defined (__hpux__) || defined (__sun__) || defined (_AIX)
1102 #   define bzero(s,n) memset (s,0,n)
1103 #endif
1104 
1105 /* POSIX does not specify how to open the master side of a terminal.Several
1106    methods are available (system specific):
1107       1- using a cloning device (USE_CLONE_DEVICE)
1108       2- getpt                  (USE_GETPT)
1109       3- openpty                (USE_OPENPTY)
1110 
1111    When using the cloning device method, the macro USE_CLONE_DEVICE should
1112    contains a full path to the adequate device.
1113 
1114    When a new system is about to be supported, one of the previous macro should
1115    be set otherwise allocate_pty_desc will return an error
1116 */
1117 
1118 /* Configurable part */
1119 #if defined (__APPLE__) || defined (BSD)
1120 #define USE_OPENPTY
1121 #elif defined (__linux__)
1122 #define USE_GETPT
1123 #elif defined (__sun__)
1124 #define USE_CLONE_DEVICE "/dev/ptmx"
1125 #elif defined (_AIX)
1126 #define USE_CLONE_DEVICE "/dev/ptc"
1127 #elif defined (__hpux__)
1128 /* On HP-UX we use the streamed version. Using the non streamed version is not
1129    recommanded (through "/dev/ptym/clone"). Indeed it seems that there are
1130    issues to detect process terminations. */
1131 #define USE_CLONE_DEVICE "/dev/ptmx"
1132 #endif
1133 
1134 /* structure that holds information about the terminal used and the process
1135    connected on the slave side */
1136 typedef struct pty_desc_struct {
1137    int  master_fd;     /* fd of the master side if the terminal */
1138    int  slave_fd;      /* fd of the slave side */
1139    char slave_name[32];   /* filename of the slave side */
1140    int  child_pid;     /* PID of the child process connected to the slave side
1141                          of the terminal */
1142 } pty_desc;
1143 
1144 /* allocate_pty_desc - allocate a pseudo terminal
1145  *
1146  * PARAMETERS
1147  *   out desc  returned pointer to a pty_desc structure containing information
1148  *             about the opened pseudo terminal
1149  * RETURN VALUE
1150  *   -1        if failed
1151  *    0        if ok
1152  * COMMENTS
1153  *   If the function is successful we should have at least the master side fd
1154  *   and the slave side filename. On some system, the slave side will also be
1155  *   opened. If this is not the case the slave side will be open once we are in
1156  *   the child process (note that opening the slave side at this stage will
1157  *   failed...).
1158  */
1159 
1160 extern char* ptsname (int);
1161 
1162 static int
allocate_pty_desc(pty_desc ** desc)1163 allocate_pty_desc (pty_desc **desc) {
1164 
1165    pty_desc *result;
1166    int  status      =  0;
1167    int  slave_fd    = -1;
1168    int  master_fd   = -1;
1169    char *slave_name = NULL;
1170 
1171 #ifdef USE_GETPT
1172   master_fd = getpt ();
1173 #elif defined (USE_OPENPTY)
1174   status = openpty (&master_fd, &slave_fd, NULL, NULL, NULL);
1175 #elif defined (USE_CLONE_DEVICE)
1176   master_fd = open (USE_CLONE_DEVICE, O_RDWR | O_NONBLOCK, 0);
1177 #else
1178   printf ("[error]: terminal support is not configured\n");
1179   return -1;
1180 #endif
1181 
1182   /* at this stage we should have the master side fd and status should be 0 */
1183   if (status != 0 || master_fd < 0)
1184     {
1185       /* If this is not the case close all opened files and return -1 */
1186       printf ("[error]: cannot allocate master side of the pty\n");
1187       if (master_fd >= 0) close (master_fd);
1188       if (slave_fd  >= 0) close (slave_fd);
1189       *desc = NULL;
1190       return -1;
1191     }
1192 
1193   /* retrieve the file name of the slave side if necessary */
1194   if (slave_name == NULL) slave_name = (char *) ptsname (master_fd);
1195 
1196   /* Now we should have slave file name */
1197   if (slave_name == NULL)
1198     {
1199       /* If not the case close any opened file and return - 1 */
1200       printf ("[error]: cannot allocate slave side of the pty\n");
1201       if (master_fd >= 0) close (master_fd);
1202       if (slave_fd  >= 0) close (slave_fd);
1203       *desc = NULL;
1204       return -1;
1205     }
1206 
1207 #if !defined(__rtems__)
1208   /* grant access to the slave side */
1209   grantpt (master_fd);
1210   /* unlock the terminal */
1211   unlockpt (master_fd);
1212 #endif
1213 
1214   /* set desc and return 0 */
1215   result = malloc (sizeof (pty_desc));
1216   result->master_fd  = master_fd;
1217   result->slave_fd   = slave_fd;
1218   /* the string returned by ptsname or _getpty is a static allocated string. So
1219      we should make a copy */
1220   strncpy (result->slave_name, slave_name, sizeof (result->slave_name));
1221   result->slave_name[sizeof (result->slave_name) - 1] = '\0';
1222   result->child_pid  = -1;
1223   *desc=result;
1224   return 0;
1225 }
1226 
1227 /* some utility macro that make the code of child_setup_tty easier to read */
1228 #define __enable(a, b) ((a) |= (b))
1229 #define __disable(a, b) ((a) &= ~(b))
1230 
1231 /* some properties do not exist on all systems. Set their value to 0 in that
1232    case */
1233 #ifndef IUCLC
1234 #define IUCLC 0
1235 #endif
1236 #ifndef OLCUC
1237 #define OLCUC 0
1238 #endif
1239 #ifndef NLDLY
1240 #define NLDLY 0
1241 #define CRDLY 0
1242 #define TABDLY 0
1243 #define BSDLY 0
1244 #define VTDLY 0
1245 #define FFDLY 0
1246 #endif
1247 
1248 /* child_setup_tty - set terminal properties
1249  *
1250  * PARAMETERS
1251  *   file descriptor of the slave side of the terminal
1252  *
1253  * RETURN VALUE
1254  *   0 if success, any other value if failed.
1255  *
1256  * COMMENTS
1257  *   None
1258  */
1259 static int
child_setup_tty(int fd)1260 child_setup_tty (int fd)
1261 {
1262   struct termios s;
1263   int    status;
1264 
1265   /* ensure that s is filled with 0 */
1266   bzero (&s, sizeof (s));
1267 
1268   /* Get the current terminal settings */
1269   status = tcgetattr (fd, &s);
1270   if (status != 0) return -1;
1271 
1272   /* Adjust input modes */
1273   __disable (s.c_iflag, IUCLC);    /* don't transform to lower case */
1274   __disable (s.c_iflag, ISTRIP);   /* don't delete 8th bit */
1275 
1276   /* Adjust output modes */
1277   __enable  (s.c_oflag, OPOST);    /* enable postprocessing */
1278   __disable (s.c_oflag, ONLCR);    /* don't map LF to CR-LF */
1279   __disable (s.c_oflag, NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
1280                                    /* disable delays */
1281   __disable (s.c_oflag, OLCUC);    /* don't transform to upper case */
1282 
1283   /* Adjust control modes */
1284   s.c_cflag = (s.c_cflag & ~CSIZE) | CS8; /* Don't strip 8th bit */
1285 
1286   /* Adjust local modes */
1287   __disable (s.c_lflag, ECHO);     /* disable echo */
1288   __enable  (s.c_lflag, ISIG);     /* enable signals */
1289   __enable  (s.c_lflag, ICANON);   /* erase/kill/eof processing */
1290 
1291   /* Adjust control characters */
1292   /* IMPORTANT: we need to ensure that Ctrl-C will trigger an interrupt signal
1293      otherwise send_signal_via_characters will fail */
1294   s.c_cc[VEOF]   = 04;         /* insure that EOF is Control-D */
1295   s.c_cc[VERASE] = CDISABLE;   /* disable erase processing */
1296   s.c_cc[VKILL]  = CDISABLE;   /* disable kill processing */
1297   s.c_cc[VQUIT]  = 28;         /* Control-\ */
1298   s.c_cc[VINTR]  = 03;         /* Control-C */
1299   s.c_cc[VEOL]   = CDISABLE;
1300   s.c_cc[VSUSP]  = 26;         /* Control-Z */
1301 
1302   /* push our changes */
1303   status = tcsetattr (fd, TCSADRAIN, &s);
1304   return status;
1305 }
1306 
1307 /* __gnat_setup_communication - interface to the external world. Should be
1308  * called before forking. On Unixes this function only call allocate_pty_desc.
1309  * The Windows implementation (in different part of this file) is very
1310  * different.
1311  *
1312  * PARAMETERS
1313  *  out desc   returned pointer to a pty_desc structure
1314  * RETURN VALUE
1315  *  0 if success, -1 otherwise
1316  */
__gnat_setup_communication(pty_desc ** desc)1317 int __gnat_setup_communication (pty_desc** desc) {
1318   return allocate_pty_desc (desc);
1319 }
1320 
1321 /* __gnat_setup_parent_communication - interface to the external world. Should
1322  * be called after forking in the parent process
1323  *
1324  * PARAMETERS
1325  *   out in_fd
1326      out out_fd
1327      out err_fd fds corresponding to the parent side of the
1328                 terminal
1329      in pid_out child process pid
1330  * RETRUN VALUE
1331  *  0
1332  */
1333 void
__gnat_setup_parent_communication(pty_desc * desc,int * in_fd,int * out_fd,int * err_fd,int * pid_out)1334 __gnat_setup_parent_communication
1335   (pty_desc *desc,
1336    int*     in_fd,  /* input */
1337    int*     out_fd, /* output */
1338    int*     err_fd, /* error */
1339    int*     pid_out)
1340 {
1341 
1342   *in_fd = desc->master_fd;
1343   *out_fd= desc->master_fd;
1344   *err_fd= desc->master_fd;
1345   desc->child_pid = *pid_out;
1346 }
1347 
1348 /* __gnat_setup_winsize - Sets up the size of the terminal
1349  * This lets the process know the size of the terminal
1350  */
1351 
__gnat_setup_winsize(pty_desc * desc,int rows,int columns)1352 void __gnat_setup_winsize (pty_desc *desc, int rows, int columns) {
1353 #ifdef TIOCGWINSZ
1354   struct winsize s;
1355   s.ws_row = (unsigned short)rows;
1356   s.ws_col = (unsigned short)columns;
1357   s.ws_xpixel = 0;
1358   s.ws_ypixel = 0;
1359   ioctl (desc->master_fd, TIOCSWINSZ, &s);
1360 #ifdef SIGWINCH
1361   if (desc->child_pid > 0) {
1362      /* Let the process know about the change in size */
1363      kill (desc->child_pid, SIGWINCH);
1364   }
1365 #endif
1366 #endif
1367 }
1368 
1369 /* __gnat_setup_child_communication - interface to external world. Should be
1370  * called after forking in the child process. On Unixes, this function
1371  * first adjust the line setting, set standard output, input and error and
1372  * then spawn the program.
1373  *
1374  * PARAMETERS
1375  *   desc      a pty_desc structure containing the pty parameters
1376  *   new_argv  argv of the program to be spawned
1377  * RETURN VALUE
1378  *   this function should not return
1379  */
1380 int
__gnat_setup_child_communication(pty_desc * desc,char ** new_argv,int Use_Pipes)1381 __gnat_setup_child_communication
1382    (pty_desc *desc,
1383     char **new_argv,
1384     int Use_Pipes)
1385 {
1386   int status;
1387   int pid = getpid ();
1388 
1389   setsid ();
1390 
1391   /* open the slave side of the terminal if necessary */
1392   if (desc->slave_fd == -1)
1393 #if defined (_AIX)
1394     /* On AIX, if the slave process is not opened with O_NDELAY or O_NONBLOCK
1395        then we might have some processes hanging on I/O system calls. Not sure
1396        we can do that for all platforms so do it only on AIX for the moment.
1397        On AIX O_NONBLOCK and O_NDELAY have slightly different meanings. When
1398        reading on the slave fd, in case there is no data available, if O_NDELAY
1399        is set then 0 is returned. If O_NON_BLOCK is -1 is returned. It seems
1400        that interactive programs such as GDB prefer the O_NDELAY behavior.
1401        We chose O_NONBLOCK because it allows us to make the distinction
1402        between a true EOF and an EOF returned because there is no data
1403        available to be read.  */
1404     desc->slave_fd = open (desc->slave_name, O_RDWR | O_NONBLOCK, 0);
1405 #else
1406     desc->slave_fd = open (desc->slave_name, O_RDWR, 0);
1407 #endif
1408 
1409 #if defined (__sun__) || defined (__hpux__)
1410   /* On systems such as Solaris we are using stream. We need to push the right
1411      "modules" in order to get the expected terminal behaviors. Otherwise
1412      functionalities such as termios are not available.  */
1413   ioctl (desc->slave_fd, I_PUSH, "ptem");
1414   ioctl (desc->slave_fd, I_PUSH, "ldterm");
1415   ioctl (desc->slave_fd, I_PUSH, "ttcompat");
1416 #endif
1417 
1418 #ifdef TIOCSCTTY
1419   /* make the tty the controlling terminal */
1420   status = ioctl (desc->slave_fd, TIOCSCTTY, 0);
1421 #endif
1422 
1423   /* adjust tty settings */
1424   child_setup_tty (desc->slave_fd);
1425   __gnat_setup_winsize (desc, 24, 80); /* To prevent errors in some shells */
1426 
1427   /* stdin, stdout and stderr should be now our tty */
1428   dup2 (desc->slave_fd, 0);
1429   dup2 (desc->slave_fd, 1);
1430   dup2 (desc->slave_fd, 2);
1431   if (desc->slave_fd > 2) close (desc->slave_fd);
1432 
1433   /* adjust process group settings */
1434   status = setpgid (pid, pid);
1435   status = tcsetpgrp (0, pid);
1436 
1437   /* launch the program */
1438   execvp (new_argv[0], new_argv);
1439 
1440   /* return the pid */
1441   return pid;
1442 }
1443 
1444 /* send_signal_via_characters - Send a characters that will trigger a signal
1445  * in the child process.
1446  *
1447  * PARAMETERS
1448  *  desc  a pty_desc structure containing terminal information
1449  *  int   a signal number
1450  * RETURN VALUE
1451  *  None
1452  */
1453 static void
send_signal_via_characters(pty_desc * desc,int signal_number)1454 send_signal_via_characters
1455   (pty_desc *desc,
1456    int signal_number)
1457 {
1458   char ctrl_c         = 03;
1459   char ctrl_backslash = 28;
1460   char ctrl_Z         = 26;
1461 
1462   switch (signal_number)
1463     {
1464       case SIGINT:
1465 	write (desc->master_fd, &ctrl_c, 1); return;
1466       case SIGQUIT:
1467 	write (desc->master_fd, &ctrl_backslash, 1); return;
1468       case SIGTSTP:
1469 	write (desc->master_fd, &ctrl_Z, 1); return;
1470     }
1471 }
1472 
1473 /* __gnat_interrupt_process - interrupt the child process
1474  *
1475  * PARAMETERS
1476  *   desc a pty_desc structure
1477  */
1478 int
__gnat_interrupt_process(pty_desc * desc)1479 __gnat_interrupt_process (pty_desc *desc)
1480 {
1481   send_signal_via_characters (desc, SIGINT);
1482   return 0;
1483 }
1484 
1485 /* __gnat_interrupt_pid - interrupt a process group
1486  *
1487  * PARAMETERS
1488  *   pid  pid of the process to interrupt
1489  */
1490 int
__gnat_interrupt_pid(int pid)1491 __gnat_interrupt_pid (int pid)
1492 {
1493   kill (-pid, SIGINT);
1494   return 0;
1495 }
1496 
1497 /* __gnat_terminate_process - kill a child process
1498  *
1499  * PARAMETERS
1500  *   desc pty_desc structure
1501  */
__gnat_terminate_process(pty_desc * desc)1502 int __gnat_terminate_process (pty_desc *desc)
1503 {
1504   return kill (desc->child_pid, SIGKILL);
1505 }
1506 
1507 /* __gnat_tty_waitpid - wait for the child process to die
1508  *
1509  * PARAMETERS
1510  *   desc pty_desc structure
1511  * RETURN VALUE
1512  *   exit status of the child process
1513  */
1514 int
__gnat_tty_waitpid(pty_desc * desc)1515 __gnat_tty_waitpid (pty_desc *desc)
1516 {
1517   int status = 0;
1518   waitpid (desc->child_pid, &status, 0);
1519   return WEXITSTATUS (status);
1520 }
1521 
1522 /* __gnat_tty_supported - Are tty supported ?
1523  *
1524  * RETURN VALUE
1525  *   always 1 on Unix systems
1526  */
1527 int
__gnat_tty_supported(void)1528 __gnat_tty_supported (void)
1529 {
1530   return 1;
1531 }
1532 
1533 /* __gnat_free_process - free a pty_desc structure
1534  *
1535  * PARAMETERS
1536  *   in out desc: a pty desc structure
1537  */
1538 void
__gnat_free_process(pty_desc ** desc)1539 __gnat_free_process (pty_desc** desc)
1540 {
1541   free (*desc);
1542   *desc = NULL;
1543 }
1544 
1545 /* __gnat_send_header - dummy function. this interface is only used on Windows */
1546 void
__gnat_send_header(pty_desc * desc,char header[5],int size,int * ret)1547 __gnat_send_header (pty_desc* desc, char header[5], int size, int *ret)
1548 {
1549   *ret = 0;
1550 }
1551 
1552 /* __gnat_reset_tty - reset line setting
1553  *
1554  * PARAMETERS
1555  *   desc: a pty_desc structure
1556  */
1557 void
__gnat_reset_tty(pty_desc * desc)1558 __gnat_reset_tty (pty_desc* desc)
1559 {
1560   child_setup_tty (desc->master_fd);
1561 }
1562 
1563 /* __gnat_new_tty - allocate a new terminal
1564  *
1565  * RETURN VALUE
1566  *   a pty_desc structure
1567  */
1568 pty_desc *
__gnat_new_tty(void)1569 __gnat_new_tty (void)
1570 {
1571   int status;
1572   pty_desc* desc;
1573   status = allocate_pty_desc (&desc);
1574   child_setup_tty (desc->master_fd);
1575   return desc;
1576 }
1577 
1578 /* __gnat_close_tty - close a terminal
1579  *
1580  * PARAMETERS
1581  *   desc  a pty_desc strucure
1582  */
__gnat_close_tty(pty_desc * desc)1583 void __gnat_close_tty (pty_desc* desc)
1584 {
1585   if (desc->master_fd >= 0) close (desc->master_fd);
1586   if (desc->slave_fd  >= 0) close (desc->slave_fd);
1587 }
1588 
1589 /* __gnat_tty_name - return slave side device name
1590  *
1591  * PARAMETERS
1592  *   desc  a pty_desc strucure
1593  * RETURN VALUE
1594  *   a string
1595  */
1596 char *
__gnat_tty_name(pty_desc * desc)1597 __gnat_tty_name (pty_desc* desc)
1598 {
1599   return desc->slave_name;
1600 }
1601 
1602 /* __gnat_tty_name - return master side fd
1603  *
1604  * PARAMETERS
1605  *   desc  a pty_desc strucure
1606  * RETURN VALUE
1607  *   a fd
1608  */
1609 int
__gnat_tty_fd(pty_desc * desc)1610 __gnat_tty_fd (pty_desc* desc)
1611 {
1612   return desc->master_fd;
1613 }
1614 
1615 #endif /* WIN32 */
1616