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