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