1 /****************************************************************************
3 * GNAT RUN-TIME COMPONENTS *
7 * C Implementation File *
9 * Copyright (C) 2008-2019, AdaCore *
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. *
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. *
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/>. *
27 * GNAT was originally developed by the GNAT team at New York University. *
28 * Extensive contributions were provided by Ada Core Technologies Inc. *
30 ****************************************************************************/
32 #define ATTRIBUTE_UNUSED __attribute__((unused))
34 /* First all usupported platforms. Add stubs for exported routines. */
36 #if defined (VMS) || defined (__vxworks) || defined (__Lynx__) \
37 || defined (__ANDROID__) || defined (__PikeOS__) || defined(__DJGPP__)
46 __gnat_tty_name (void* t ATTRIBUTE_UNUSED
)
52 __gnat_interrupt_pid (int pid ATTRIBUTE_UNUSED
)
58 __gnat_interrupt_process (void* desc ATTRIBUTE_UNUSED
)
64 __gnat_setup_communication (void** desc 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
)
79 __gnat_setup_child_communication (void *d ATTRIBUTE_UNUSED
,
80 char **n ATTRIBUTE_UNUSED
,
81 int u ATTRIBUTE_UNUSED
)
87 __gnat_terminate_process (void *desc ATTRIBUTE_UNUSED
)
93 __gnat_terminate_pid (int pid ATTRIBUTE_UNUSED
)
99 __gnat_tty_fd (void* t ATTRIBUTE_UNUSED
)
105 __gnat_tty_supported (void)
111 __gnat_tty_waitpid (void *desc ATTRIBUTE_UNUSED
, int blocking
)
117 __gnat_close_tty (void* t ATTRIBUTE_UNUSED
)
122 __gnat_free_process (void** process ATTRIBUTE_UNUSED
)
127 __gnat_reset_tty (void* t 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
)
140 __gnat_setup_winsize (void *desc ATTRIBUTE_UNUSED
,
141 int rows ATTRIBUTE_UNUSED
,
142 int columns ATTRIBUTE_UNUSED
)
146 /* For Windows platforms. */
148 #elif defined(_WIN32)
155 #include <winternl.h>
157 #define MAXPATHLEN 1024
159 #define NILP(x) ((x) == 0)
161 #define report_file_error(x, y) fprintf (stderr, "Error: %s\n", x);
162 #define INTEGERP(x) 1
166 int pid
; /* Number of this process */
167 PROCESS_INFORMATION procinfo
;
168 HANDLE w_infd
, w_outfd
;
169 HANDLE w_forkin
, w_forkout
;
173 /* Control whether create_child cause the process to inherit GNAT Studio'
174 error mode setting. The default is 1, to minimize the possibility of
175 subprocesses blocking when accessing unmounted drives. */
176 static int Vw32_start_process_inherit_error_mode
= 1;
178 /* Control whether spawnve quotes arguments as necessary to ensure
179 correct parsing by child process. Because not all uses of spawnve
180 are careful about constructing argv arrays, we make this behavior
181 conditional (off by default, since a similar operation is already done
182 in g-expect.adb by calling Normalize_Argument). */
183 static int Vw32_quote_process_args
= 0;
185 static DWORD
AbsoluteSeek(HANDLE
, DWORD
);
186 static VOID
ReadBytes(HANDLE
, LPVOID
, DWORD
);
188 #define XFER_BUFFER_SIZE 2048
190 /* This tell if the executable we're about to launch uses a GUI interface. */
191 /* if we can't determine it, we will return true */
193 is_gui_app (char *exe
)
200 DWORD CoffHeaderOffset
;
201 DWORD MoreDosHeader
[16];
207 IMAGE_DOS_HEADER image_dos_header
;
208 IMAGE_FILE_HEADER image_file_header
;
209 IMAGE_OPTIONAL_HEADER image_optional_header
;
210 IMAGE_SECTION_HEADER image_section_header
;
213 * Open the reference file.
221 file
= malloc ((nlen
+ 1) * sizeof (char));
222 memcpy (file
, &exe
[1], nlen
);
226 hImage
= CreateFile(file
,
231 FILE_ATTRIBUTE_NORMAL
,
238 if (INVALID_HANDLE_VALUE
== hImage
)
240 report_file_error ("Could not open exe: ", Qnil
);
241 report_file_error (exe
, Qnil
);
242 report_file_error ("\n", Qnil
);
243 CloseHandle (hImage
);
248 * Read the MS-DOS image header.
250 ReadBytes(hImage
, &image_dos_header
, sizeof(IMAGE_DOS_HEADER
));
252 if (IMAGE_DOS_SIGNATURE
!= image_dos_header
.e_magic
)
254 report_file_error("Sorry, I do not understand this file.\n", Qnil
);
255 CloseHandle (hImage
);
260 * Read more MS-DOS header. */
261 ReadBytes(hImage
, MoreDosHeader
, sizeof(MoreDosHeader
));
263 * Get actual COFF header.
265 CoffHeaderOffset
= AbsoluteSeek(hImage
, image_dos_header
.e_lfanew
) +
267 if (CoffHeaderOffset
< 0) {
268 CloseHandle (hImage
);
272 ReadBytes (hImage
, &ntSignature
, sizeof(ULONG
));
274 if (IMAGE_NT_SIGNATURE
!= ntSignature
)
276 report_file_error ("Missing NT signature. Unknown file type.\n", Qnil
);
277 CloseHandle (hImage
);
281 SectionOffset
= CoffHeaderOffset
+ IMAGE_SIZEOF_FILE_HEADER
+
282 IMAGE_SIZEOF_NT_OPTIONAL_HEADER
;
284 ReadBytes(hImage
, &image_file_header
, IMAGE_SIZEOF_FILE_HEADER
);
287 * Read optional header.
290 &image_optional_header
,
291 IMAGE_SIZEOF_NT_OPTIONAL_HEADER
);
293 CloseHandle (hImage
);
295 switch (image_optional_header
.Subsystem
)
297 case IMAGE_SUBSYSTEM_UNKNOWN
:
300 case IMAGE_SUBSYSTEM_NATIVE
:
303 case IMAGE_SUBSYSTEM_WINDOWS_GUI
:
306 case IMAGE_SUBSYSTEM_WINDOWS_CUI
:
309 case IMAGE_SUBSYSTEM_OS2_CUI
:
312 case IMAGE_SUBSYSTEM_POSIX_CUI
:
316 /* Unknown, return GUI app to be preservative: if yes, it will be
317 correctly launched, if no, it will be launched, and a console will
318 be also displayed, which is not a big deal */
325 AbsoluteSeek (HANDLE hFile
, DWORD offset
)
329 newOffset
= SetFilePointer (hFile
, offset
, NULL
, FILE_BEGIN
);
331 if (newOffset
== 0xFFFFFFFF)
338 ReadBytes (HANDLE hFile
, LPVOID buffer
, DWORD size
)
342 if (!ReadFile(hFile
, buffer
, size
, &bytes
, NULL
))
347 else if (size
!= bytes
)
354 nt_spawnve (char *exe
, char **argv
, char *env
, struct TTY_Process
*process
)
357 SECURITY_ATTRIBUTES sec_attrs
;
358 SECURITY_DESCRIPTOR sec_desc
;
360 char dir
[ MAXPATHLEN
];
363 char *cmdline
, *parg
, **targ
;
368 /* we have to do some conjuring here to put argv and envp into the
369 form CreateProcess wants... argv needs to be a space separated/null
370 terminated list of parameters, and envp is a null
371 separated/double-null terminated list of parameters.
373 Additionally, zero-length args and args containing whitespace or
374 quote chars need to be wrapped in double quotes - for this to work,
375 embedded quotes need to be escaped as well. The aim is to ensure
376 the child process reconstructs the argv array we start with
377 exactly, so we treat quotes at the beginning and end of arguments
380 Note that using backslash to escape embedded quotes requires
381 additional special handling if an embedded quote is already
382 preceded by backslash, or if an arg requiring quoting ends with
383 backslash. In such cases, the run of escape characters needs to be
384 doubled. For consistency, we apply this special handling as long
385 as the escape character is not quote.
387 Since we have no idea how large argv and envp are likely to be we
388 figure out list lengths on the fly and allocate them. */
390 if (!NILP (Vw32_quote_process_args
))
393 /* Override escape char by binding w32-quote-process-args to
394 desired character, or use t for auto-selection. */
395 if (INTEGERP (Vw32_quote_process_args
))
396 escape_char
= XINT (Vw32_quote_process_args
);
408 int escape_char_run
= 0;
416 /* allow for embedded quotes to be escaped */
419 /* handle the case where the embedded quote is already escaped */
420 if (escape_char_run
> 0)
422 /* To preserve the arg exactly, we need to double the
423 preceding escape characters (plus adding one to
424 escape the quote character itself). */
425 arglen
+= escape_char_run
;
428 else if (*p
== ' ' || *p
== '\t')
433 if (*p
== escape_char
&& escape_char
!= '"')
441 /* handle the case where the arg ends with an escape char - we
442 must not let the enclosing quote be escaped. */
443 if (escape_char_run
> 0)
444 arglen
+= escape_char_run
;
446 arglen
+= strlen (*targ
) + 1;
450 is_gui
= is_gui_app (argv
[0]);
454 /* could not determine application type. Try launching with "cmd /c" */
460 cmdline
= (char*)malloc (arglen
+ 1);
464 if (use_cmd
== TRUE
) {
465 strcpy (parg
, "cmd /c ");
480 if (*p
== ' ' || *p
== '\t' || *p
== '"')
485 int escape_char_run
= 0;
491 last
= p
+ strlen (p
) - 1;
497 /* double preceding escape chars if any */
498 while (escape_char_run
> 0)
500 *parg
++ = escape_char
;
503 /* escape all quote chars, even at beginning or end */
504 *parg
++ = escape_char
;
508 if (*p
== escape_char
&& escape_char
!= '"')
513 /* double escape chars before enclosing quote */
514 while (escape_char_run
> 0)
516 *parg
++ = escape_char
;
523 strcpy (parg
, *targ
);
524 parg
+= strlen (*targ
);
531 memset (&start
, 0, sizeof (start
));
532 start
.cb
= sizeof (start
);
534 if (process
->usePipe
== TRUE
) {
535 start
.dwFlags
= STARTF_USESTDHANDLES
;
536 start
.hStdInput
= process
->w_forkin
;
537 start
.hStdOutput
= process
->w_forkout
;
538 /* child's stderr is always redirected to outfd */
539 start
.hStdError
= process
->w_forkout
;
541 start
.dwFlags
= STARTF_USESTDHANDLES
;
542 /* We only need to redirect stderr/stdout here. Stdin will be forced to
543 the spawned process console by explaunch */
544 start
.hStdInput
= NULL
;
545 start
.hStdOutput
= process
->w_forkout
;
546 start
.hStdError
= process
->w_forkout
;
549 /* Explicitly specify no security */
550 if (!InitializeSecurityDescriptor (&sec_desc
, SECURITY_DESCRIPTOR_REVISION
))
552 if (!SetSecurityDescriptorDacl (&sec_desc
, TRUE
, NULL
, FALSE
))
554 sec_attrs
.nLength
= sizeof (sec_attrs
);
555 sec_attrs
.lpSecurityDescriptor
= &sec_desc
;
556 sec_attrs
.bInheritHandle
= FALSE
;
558 /* creating a new console allow easier close. Do not use
559 CREATE_NEW_PROCESS_GROUP as this results in disabling Ctrl+C */
560 flags
= CREATE_NEW_CONSOLE
;
561 if (NILP (Vw32_start_process_inherit_error_mode
))
562 flags
|= CREATE_DEFAULT_ERROR_MODE
;
564 /* if app is not a gui application, hide the console */
565 if (is_gui
== FALSE
) {
566 start
.dwFlags
|= STARTF_USESHOWWINDOW
;
567 start
.wShowWindow
= SW_HIDE
;
570 /* Set initial directory to null character to use current directory */
571 if (!CreateProcess (NULL
, cmdline
, &sec_attrs
, NULL
, TRUE
,
572 flags
, env
, NULL
, &start
, &process
->procinfo
))
575 pid
= (int) process
->procinfo
.hProcess
;
584 /*************************
585 ** __gnat_send_header ()
586 *************************/
588 #define EXP_SLAVE_CREATE 'c'
589 #define EXP_SLAVE_KEY 'k'
590 #define EXP_SLAVE_MOUSE 'm'
591 #define EXP_SLAVE_WRITE 'w'
592 #define EXP_SLAVE_KILL 'x'
594 #define EXP_KILL_TERMINATE 0x1
595 #define EXP_KILL_CTRL_C 0x2
596 #define EXP_KILL_CTRL_BREAK 0x4
599 __gnat_send_header (struct TTY_Process
* p
, char header
[5], int size
, int *ret
)
601 if (p
->usePipe
== FALSE
) {
602 header
[0] = EXP_SLAVE_WRITE
;
603 header
[1] = size
& 0xff;
604 header
[2] = (size
& 0xff00) >> 8;
605 header
[3] = (size
& 0xff0000) >> 16;
606 header
[4] = (size
& 0xff000000) >> 24;
613 /**********************************
614 ** __gnat_setup_communication ()
615 **********************************/
618 __gnat_setup_communication (struct TTY_Process
** process_out
) /* output param */
620 struct TTY_Process
* process
;
622 process
= (struct TTY_Process
*)malloc (sizeof (struct TTY_Process
));
623 ZeroMemory (process
, sizeof (struct TTY_Process
));
624 *process_out
= process
;
629 #define EXP_PIPE_BASENAME "\\\\.\\pipe\\ExpectPipe"
632 __gnat_setup_child_communication
633 (struct TTY_Process
* process
,
639 SECURITY_ATTRIBUTES sec_attrs
;
640 char slavePath
[MAX_PATH
];
644 char pipeNameIn
[100];
645 HANDLE hSlaveInDrv
= NULL
; /* Handle to communicate with slave driver */
647 parent
= GetCurrentProcess ();
649 /* Set inheritance for the pipe handles */
650 sec_attrs
.nLength
= sizeof (SECURITY_ATTRIBUTES
);
651 sec_attrs
.bInheritHandle
= TRUE
;
652 sec_attrs
.lpSecurityDescriptor
= NULL
;
655 /* Create in and out pipes */
656 if (!CreatePipe (&process
->w_forkin
, &process
->w_infd
, &sec_attrs
, 0))
657 report_file_error ("Creation of child's IN handle", Qnil
);
658 if (!CreatePipe (&process
->w_outfd
, &process
->w_forkout
, &sec_attrs
, 0))
659 report_file_error ("Creation of child's OUT handle", Qnil
);
661 /* Do not inherit the parent's side of the pipes */
662 SetHandleInformation (&process
->w_infd
, HANDLE_FLAG_INHERIT
, 0);
663 SetHandleInformation (&process
->w_outfd
, HANDLE_FLAG_INHERIT
, 0);
665 /* use native argv */
667 process
->usePipe
= TRUE
;
670 static int pipeNameId
= 0;
672 process
->w_infd
= NULL
;
674 /* We create a named pipe for Input, as we handle input by sending special
675 commands to the explaunch process, that uses it to feed the actual input
677 sprintf(pipeNameIn
, "%sIn%08x_%08x", EXP_PIPE_BASENAME
,
678 GetCurrentProcessId(), pipeNameId
);
681 hSlaveInDrv
= CreateNamedPipe(pipeNameIn
,
682 PIPE_ACCESS_OUTBOUND
,
683 PIPE_TYPE_BYTE
| PIPE_WAIT
, 1, 8192, 8192,
685 if (hSlaveInDrv
== NULL
) goto end
;
687 if (!CreatePipe (&process
->w_outfd
, &process
->w_forkout
, &sec_attrs
, 0))
688 report_file_error ("Creation of child's OUT handle", Qnil
);
690 if (SearchPath (NULL
, "explaunch.exe", NULL
,
691 MAX_PATH
, slavePath
, NULL
) == 0) goto end
;
693 for (argc
=0; argv
[argc
] != NULL
; argc
++) ;
694 nargv
= (char **) malloc (sizeof (char*) * (argc
+ 3));
695 nargv
[0] = slavePath
;
696 nargv
[1] = pipeNameIn
;
698 for (i
= 0; i
<= argc
; i
++) nargv
[i
+ 2] = argv
[i
];
699 process
->usePipe
= FALSE
;
702 /* Spawn the child. */
703 cpid
= nt_spawnve (nargv
[0], nargv
, NULL
, process
);
705 /* close the duplicated handles passed to the child */
706 CloseHandle (process
->w_forkout
);
708 if (process
->usePipe
== TRUE
) {
709 CloseHandle (process
->w_forkin
);
712 UCHAR buf
[8]; /* enough space for child status info */
718 * Wait for connection with the slave driver
720 bRet
= ConnectNamedPipe(hSlaveInDrv
, NULL
);
722 dwRet
= GetLastError();
723 if (dwRet
== ERROR_PIPE_CONNECTED
) {
730 process
->w_infd
= hSlaveInDrv
;
733 * wait for slave driver to initialize before allowing user to send to it
735 bRet
= ReadFile(process
->w_outfd
, buf
, 8, &count
, NULL
);
740 dwRet
= buf
[0] | (buf
[1] << 8) | (buf
[2] << 16) | (buf
[3] << 24);
745 cpid
= buf
[4] | (buf
[5] << 8) | (buf
[6] << 16) | (buf
[7] << 24);
750 /* An error occurred while trying to spawn the process. */
751 report_file_error ("Spawning child process", Qnil
);
755 if (hSlaveInDrv
!= NULL
)
756 CloseHandle (hSlaveInDrv
);
761 __gnat_setup_parent_communication
762 (struct TTY_Process
* process
,
768 *in
= _open_osfhandle ((long) process
->w_infd
, 0);
769 *out
= _open_osfhandle ((long) process
->w_outfd
, 0);
770 /* child's stderr is always redirected to outfd */
775 typedef struct _child_process
778 PROCESS_INFORMATION
*procinfo
;
781 /* The major and minor versions of NT. */
782 static int w32_major_version
;
783 static int w32_minor_version
;
785 /* Distinguish between Windows NT and Windows 95. */
786 static enum {OS_UNKNOWN
, OS_WIN95
, OS_NT
} os_subtype
= OS_UNKNOWN
;
788 /* Cache information describing the NT system for later use. */
790 cache_system_info (void)
803 /* Cache the version of the operating system. */
804 version
.data
= GetVersion ();
805 w32_major_version
= version
.info
.major
;
806 w32_minor_version
= version
.info
.minor
;
808 if (version
.info
.platform
& 0x8000)
809 os_subtype
= OS_WIN95
;
815 find_child_console (HWND hwnd
, child_process
* cp
)
820 thread_id
= GetWindowThreadProcessId (hwnd
, &process_id
);
821 if (process_id
== cp
->procinfo
->dwProcessId
)
823 char window_class
[32];
825 GetClassName (hwnd
, window_class
, sizeof (window_class
));
826 if (strcmp (window_class
,
827 (os_subtype
== OS_WIN95
)
829 : "ConsoleWindowClass") == 0)
840 __gnat_interrupt_process (struct TTY_Process
* p
)
846 if (p
->usePipe
== TRUE
) {
849 buf
[0] = EXP_SLAVE_KILL
;
850 buf
[1] = EXP_KILL_CTRL_C
;
851 bret
= WriteFile (p
->w_infd
, buf
, 2, &written
, NULL
);
855 return __gnat_interrupt_pid (p
->procinfo
.dwProcessId
);
861 __gnat_interrupt_pid (int pid
)
863 volatile child_process cp
;
866 cp
.procinfo
= (LPPROCESS_INFORMATION
) malloc (sizeof (PROCESS_INFORMATION
));
867 cp
.procinfo
->dwProcessId
= pid
;
869 if (os_subtype
== OS_UNKNOWN
)
870 cache_system_info ();
872 /* Try to locate console window for process. */
873 EnumWindows ((WNDENUMPROC
) find_child_console
, (LPARAM
) &cp
);
877 BYTE control_scan_code
= (BYTE
) MapVirtualKey (VK_CONTROL
, 0);
878 /* Retrieve Ctrl-C scancode */
879 BYTE vk_break_code
= 'C';
880 BYTE break_scan_code
= (BYTE
) MapVirtualKey (vk_break_code
, 0);
881 HWND foreground_window
;
883 foreground_window
= GetForegroundWindow ();
884 if (foreground_window
)
886 /* NT 5.0, and apparently also Windows 98, will not allow
887 a Window to be set to foreground directly without the
888 user's involvement. The workaround is to attach
889 ourselves to the thread that owns the foreground
890 window, since that is the only thread that can set the
891 foreground window. */
892 DWORD foreground_thread
, child_thread
;
895 GetWindowThreadProcessId (foreground_window
, NULL
);
896 if (foreground_thread
== GetCurrentThreadId ()
897 || !AttachThreadInput (GetCurrentThreadId (),
898 foreground_thread
, TRUE
))
899 foreground_thread
= 0;
901 child_thread
= GetWindowThreadProcessId (cp
.hwnd
, NULL
);
902 if (child_thread
== GetCurrentThreadId ()
903 || !AttachThreadInput (GetCurrentThreadId (),
907 /* Set the foreground window to the child. */
908 if (SetForegroundWindow (cp
.hwnd
))
910 /* Generate keystrokes as if user had typed Ctrl-Break or
912 keybd_event (VK_CONTROL
, control_scan_code
, 0, 0);
913 keybd_event (vk_break_code
, break_scan_code
,
914 (vk_break_code
== 'C' ? 0 : KEYEVENTF_EXTENDEDKEY
), 0);
915 keybd_event (vk_break_code
, break_scan_code
,
916 (vk_break_code
== 'C' ? 0 : KEYEVENTF_EXTENDEDKEY
)
917 | KEYEVENTF_KEYUP
, 0);
918 keybd_event (VK_CONTROL
, control_scan_code
, KEYEVENTF_KEYUP
, 0);
920 /* Sleep for a bit to give time for the main frame to respond
921 to focus change events. */
924 SetForegroundWindow (foreground_window
);
926 /* Detach from the foreground and child threads now that
927 the foreground switching is over. */
928 if (foreground_thread
)
929 AttachThreadInput (GetCurrentThreadId (), foreground_thread
, FALSE
);
931 AttachThreadInput (GetCurrentThreadId (), child_thread
, FALSE
);
934 /* Ctrl-Break is NT equivalent of SIGINT. */
935 else if (!GenerateConsoleCtrlEvent
936 (CTRL_BREAK_EVENT
, cp
.procinfo
->dwProcessId
))
946 /* kill a process, as this implementation use CreateProcess on Win32 we need
947 to use Win32 TerminateProcess API */
949 __gnat_terminate_process (struct TTY_Process
* p
)
955 if (p
->usePipe
== TRUE
) {
958 buf
[0] = EXP_SLAVE_KILL
;
959 buf
[1] = EXP_KILL_TERMINATE
;
960 bret
= WriteFile (p
->w_infd
, buf
, 2, &written
, NULL
);
964 if (!TerminateProcess (p
->procinfo
.hProcess
, 1))
978 find_process_handle (HWND hwnd
, pid_struct
* ps
)
983 thread_id
= GetWindowThreadProcessId (hwnd
, &process_id
);
984 if (process_id
== ps
->dwProcessId
)
994 __gnat_terminate_pid (int pid
)
998 ps
.dwProcessId
= pid
;
1000 EnumWindows ((WNDENUMPROC
) find_process_handle
, (LPARAM
) &ps
);
1004 if (!TerminateProcess (ps
.hwnd
, 1))
1013 /* wait for process pid to terminate and return the process status. This
1014 implementation is different from the adaint.c one for Windows as it uses
1015 the Win32 API instead of the C one. */
1018 __gnat_tty_waitpid (struct TTY_Process
* p
, int blocking
)
1021 HANDLE hprocess
= p
->procinfo
.hProcess
;
1024 /* Wait is needed on Windows only in blocking mode. */
1025 WaitForSingleObject (hprocess
, 0);
1028 GetExitCodeProcess (hprocess
, &exitcode
);
1030 if (exitcode
== STILL_ACTIVE
) {
1031 /* If process is still active return -1. */
1034 /* Process is dead, so handle to process and main thread can be closed. */
1035 CloseHandle (p
->procinfo
.hThread
);
1036 CloseHandle (hprocess
);
1039 /* No need to close the handles: they were closed on the ada side */
1040 return (int) exitcode
;
1043 /********************************
1044 ** __gnat_free_process ()
1045 ********************************/
1048 __gnat_free_process (struct TTY_Process
** process
)
1057 int tty_fd
; /* descriptor for the tty */
1058 char tty_name
[24]; /* Name of TTY device */
1062 __gnat_tty_supported (void)
1067 /* Return the tty name associated with p */
1070 __gnat_tty_name (TTY_Handle
* t
)
1076 __gnat_tty_fd (TTY_Handle
* t
)
1082 __gnat_new_tty (void)
1084 return (TTY_Handle
*)0;
1088 __gnat_reset_tty (TTY_Handle
* t
)
1094 __gnat_close_tty (TTY_Handle
* t
)
1100 __gnat_setup_winsize (void *desc
, int rows
, int columns
)
1104 #else /* defined(_WIN32, implementatin for all UNIXes */
1106 /* First defined some macro to identify easily some systems */
1107 #if defined (__FreeBSD__) \
1108 || defined (__OpenBSD__) \
1109 || defined (__NetBSD__) \
1110 || defined (__DragonFly__)
1114 /* Include every system header we need */
1119 #include <sys/ioctl.h>
1120 #include <termios.h>
1123 #include <sys/stat.h>
1124 #include <sys/types.h>
1125 #include <sys/wait.h>
1127 #if defined (__sun__)
1128 # include <sys/stropts.h>
1130 #if defined (BSD) || defined (__sun__)
1131 # include <sys/signal.h>
1133 #if defined (__hpux__)
1134 # include <sys/stropts.h>
1137 #define CDISABLE _POSIX_VDISABLE
1139 /* On HP-UX and Sun system, there is a bzero function but with a different
1140 signature. Use memset instead */
1141 #if defined (__hpux__) || defined (__sun__) || defined (_AIX)
1142 # define bzero(s,n) memset (s,0,n)
1145 /* POSIX does not specify how to open the master side of a terminal.Several
1146 methods are available (system specific):
1147 1- using a cloning device (USE_CLONE_DEVICE)
1148 2- getpt (USE_GETPT)
1149 3- openpty (USE_OPENPTY)
1151 When using the cloning device method, the macro USE_CLONE_DEVICE should
1152 contains a full path to the adequate device.
1154 When a new system is about to be supported, one of the previous macro should
1155 be set otherwise allocate_pty_desc will return an error
1158 /* Configurable part */
1159 #if defined (__APPLE__) || defined (BSD)
1161 #elif defined (__linux__)
1163 #elif defined (__sun__)
1164 #define USE_CLONE_DEVICE "/dev/ptmx"
1165 #elif defined (_AIX)
1166 #define USE_CLONE_DEVICE "/dev/ptc"
1167 #elif defined (__hpux__)
1168 /* On HP-UX we use the streamed version. Using the non streamed version is not
1169 recommanded (through "/dev/ptym/clone"). Indeed it seems that there are
1170 issues to detect process terminations. */
1171 #define USE_CLONE_DEVICE "/dev/ptmx"
1174 /* structure that holds information about the terminal used and the process
1175 connected on the slave side */
1176 typedef struct pty_desc_struct
{
1177 int master_fd
; /* fd of the master side if the terminal */
1178 int slave_fd
; /* fd of the slave side */
1179 char slave_name
[32]; /* filename of the slave side */
1180 int child_pid
; /* PID of the child process connected to the slave side
1184 /* allocate_pty_desc - allocate a pseudo terminal
1187 * out desc returned pointer to a pty_desc structure containing information
1188 * about the opened pseudo terminal
1193 * If the function is successful we should have at least the master side fd
1194 * and the slave side filename. On some system, the slave side will also be
1195 * opened. If this is not the case the slave side will be open once we are in
1196 * the child process (note that opening the slave side at this stage will
1200 extern char* ptsname (int);
1203 allocate_pty_desc (pty_desc
**desc
) {
1209 char *slave_name
= NULL
;
1212 master_fd
= getpt ();
1213 #elif defined (USE_OPENPTY)
1214 status
= openpty (&master_fd
, &slave_fd
, NULL
, NULL
, NULL
);
1215 #elif defined (USE_CLONE_DEVICE)
1216 master_fd
= open (USE_CLONE_DEVICE
, O_RDWR
| O_NONBLOCK
, 0);
1218 printf ("[error]: terminal support is not configured\n");
1222 /* at this stage we should have the master side fd and status should be 0 */
1223 if (status
!= 0 || master_fd
< 0)
1225 /* If this is not the case close all opened files and return -1 */
1226 printf ("[error]: cannot allocate master side of the pty\n");
1227 if (master_fd
>= 0) close (master_fd
);
1228 if (slave_fd
>= 0) close (slave_fd
);
1233 /* retrieve the file name of the slave side if necessary */
1234 if (slave_name
== NULL
) slave_name
= (char *) ptsname (master_fd
);
1236 /* Now we should have slave file name */
1237 if (slave_name
== NULL
)
1239 /* If not the case close any opened file and return - 1 */
1240 printf ("[error]: cannot allocate slave side of the pty\n");
1241 if (master_fd
>= 0) close (master_fd
);
1242 if (slave_fd
>= 0) close (slave_fd
);
1247 #if !defined(__rtems__)
1248 /* grant access to the slave side */
1249 grantpt (master_fd
);
1250 /* unlock the terminal */
1251 unlockpt (master_fd
);
1254 /* set desc and return 0 */
1255 result
= malloc (sizeof (pty_desc
));
1256 result
->master_fd
= master_fd
;
1257 result
->slave_fd
= slave_fd
;
1258 /* the string returned by ptsname or _getpty is a static allocated string. So
1259 we should make a copy */
1260 strncpy (result
->slave_name
, slave_name
, sizeof (result
->slave_name
));
1261 result
->slave_name
[sizeof (result
->slave_name
) - 1] = '\0';
1262 result
->child_pid
= -1;
1267 /* some utility macro that make the code of child_setup_tty easier to read */
1268 #define __enable(a, b) ((a) |= (b))
1269 #define __disable(a, b) ((a) &= ~(b))
1271 /* some properties do not exist on all systems. Set their value to 0 in that
1288 /* child_setup_tty - set terminal properties
1291 * file descriptor of the slave side of the terminal
1294 * 0 if success, any other value if failed.
1300 child_setup_tty (int fd
)
1305 /* ensure that s is filled with 0 */
1306 bzero (&s
, sizeof (s
));
1308 /* Get the current terminal settings */
1309 status
= tcgetattr (fd
, &s
);
1310 if (status
!= 0) return -1;
1312 /* Adjust input modes */
1313 __disable (s
.c_iflag
, IUCLC
); /* don't transform to lower case */
1314 __disable (s
.c_iflag
, ISTRIP
); /* don't delete 8th bit */
1316 /* Adjust output modes */
1317 __enable (s
.c_oflag
, OPOST
); /* enable postprocessing */
1318 __disable (s
.c_oflag
, ONLCR
); /* don't map LF to CR-LF */
1319 __disable (s
.c_oflag
, NLDLY
|CRDLY
|TABDLY
|BSDLY
|VTDLY
|FFDLY
);
1320 /* disable delays */
1321 __disable (s
.c_oflag
, OLCUC
); /* don't transform to upper case */
1323 /* Adjust control modes */
1324 s
.c_cflag
= (s
.c_cflag
& ~CSIZE
) | CS8
; /* Don't strip 8th bit */
1326 /* Adjust local modes */
1327 __disable (s
.c_lflag
, ECHO
); /* disable echo */
1328 __enable (s
.c_lflag
, ISIG
); /* enable signals */
1329 __enable (s
.c_lflag
, ICANON
); /* erase/kill/eof processing */
1331 /* Adjust control characters */
1332 /* IMPORTANT: we need to ensure that Ctrl-C will trigger an interrupt signal
1333 otherwise send_signal_via_characters will fail */
1334 s
.c_cc
[VEOF
] = 04; /* insure that EOF is Control-D */
1335 s
.c_cc
[VERASE
] = CDISABLE
; /* disable erase processing */
1336 s
.c_cc
[VKILL
] = CDISABLE
; /* disable kill processing */
1337 s
.c_cc
[VQUIT
] = 28; /* Control-\ */
1338 s
.c_cc
[VINTR
] = 03; /* Control-C */
1339 s
.c_cc
[VEOL
] = CDISABLE
;
1340 s
.c_cc
[VSUSP
] = 26; /* Control-Z */
1342 /* push our changes */
1343 status
= tcsetattr (fd
, TCSADRAIN
, &s
);
1347 /* __gnat_setup_communication - interface to the external world. Should be
1348 * called before forking. On Unixes this function only call allocate_pty_desc.
1349 * The Windows implementation (in different part of this file) is very
1353 * out desc returned pointer to a pty_desc structure
1355 * 0 if success, -1 otherwise
1357 int __gnat_setup_communication (pty_desc
** desc
) {
1358 return allocate_pty_desc (desc
);
1361 /* __gnat_setup_parent_communication - interface to the external world. Should
1362 * be called after forking in the parent process
1367 out err_fd fds corresponding to the parent side of the
1369 in pid_out child process pid
1374 __gnat_setup_parent_communication
1376 int* in_fd
, /* input */
1377 int* out_fd
, /* output */
1378 int* err_fd
, /* error */
1382 *in_fd
= desc
->master_fd
;
1383 *out_fd
= desc
->master_fd
;
1384 *err_fd
= desc
->master_fd
;
1385 desc
->child_pid
= *pid_out
;
1388 /* __gnat_setup_winsize - Sets up the size of the terminal
1389 * This lets the process know the size of the terminal
1392 void __gnat_setup_winsize (pty_desc
*desc
, int rows
, int columns
) {
1395 s
.ws_row
= (unsigned short)rows
;
1396 s
.ws_col
= (unsigned short)columns
;
1399 ioctl (desc
->master_fd
, TIOCSWINSZ
, &s
);
1401 if (desc
->child_pid
> 0) {
1402 /* Let the process know about the change in size */
1403 kill (desc
->child_pid
, SIGWINCH
);
1409 /* __gnat_setup_child_communication - interface to external world. Should be
1410 * called after forking in the child process. On Unixes, this function
1411 * first adjust the line setting, set standard output, input and error and
1412 * then spawn the program.
1415 * desc a pty_desc structure containing the pty parameters
1416 * new_argv argv of the program to be spawned
1418 * this function should not return
1421 __gnat_setup_child_communication
1424 int Use_Pipes ATTRIBUTE_UNUSED
)
1427 int pid
= getpid ();
1431 /* open the slave side of the terminal if necessary */
1432 if (desc
->slave_fd
== -1)
1434 /* On AIX, if the slave process is not opened with O_NDELAY or O_NONBLOCK
1435 then we might have some processes hanging on I/O system calls. Not sure
1436 we can do that for all platforms so do it only on AIX for the moment.
1437 On AIX O_NONBLOCK and O_NDELAY have slightly different meanings. When
1438 reading on the slave fd, in case there is no data available, if O_NDELAY
1439 is set then 0 is returned. If O_NON_BLOCK is -1 is returned. It seems
1440 that interactive programs such as GDB prefer the O_NDELAY behavior.
1441 We chose O_NONBLOCK because it allows us to make the distinction
1442 between a true EOF and an EOF returned because there is no data
1443 available to be read. */
1444 desc
->slave_fd
= open (desc
->slave_name
, O_RDWR
| O_NONBLOCK
, 0);
1446 desc
->slave_fd
= open (desc
->slave_name
, O_RDWR
, 0);
1449 #if defined (__sun__) || defined (__hpux__)
1450 /* On systems such as Solaris we are using stream. We need to push the right
1451 "modules" in order to get the expected terminal behaviors. Otherwise
1452 functionalities such as termios are not available. */
1453 ioctl (desc
->slave_fd
, I_PUSH
, "ptem");
1454 ioctl (desc
->slave_fd
, I_PUSH
, "ldterm");
1455 ioctl (desc
->slave_fd
, I_PUSH
, "ttcompat");
1459 /* make the tty the controlling terminal */
1460 if ((status
= ioctl (desc
->slave_fd
, TIOCSCTTY
, 0)) == -1)
1464 /* adjust tty settings */
1465 child_setup_tty (desc
->slave_fd
);
1466 __gnat_setup_winsize (desc
, 24, 80); /* To prevent errors in some shells */
1468 /* stdin, stdout and stderr should be now our tty */
1469 dup2 (desc
->slave_fd
, 0);
1470 dup2 (desc
->slave_fd
, 1);
1471 dup2 (desc
->slave_fd
, 2);
1472 if (desc
->slave_fd
> 2) close (desc
->slave_fd
);
1474 /* adjust process group settings */
1475 /* ignore failures of the following two commands as the context might not
1476 * allow making those changes. */
1480 /* launch the program */
1481 execvp (new_argv
[0], new_argv
);
1486 /* send_signal_via_characters - Send a characters that will trigger a signal
1487 * in the child process.
1490 * desc a pty_desc structure containing terminal information
1491 * int a signal number
1496 send_signal_via_characters
1501 char ctrl_backslash
= 28;
1504 switch (signal_number
)
1507 write (desc
->master_fd
, &ctrl_c
, 1); return;
1509 write (desc
->master_fd
, &ctrl_backslash
, 1); return;
1511 write (desc
->master_fd
, &ctrl_Z
, 1); return;
1515 /* __gnat_interrupt_process - interrupt the child process
1518 * desc a pty_desc structure
1521 __gnat_interrupt_process (pty_desc
*desc
)
1523 send_signal_via_characters (desc
, SIGINT
);
1527 /* __gnat_interrupt_pid - interrupt a process group
1530 * pid pid of the process to interrupt
1533 __gnat_interrupt_pid (int pid
)
1535 kill (-pid
, SIGINT
);
1539 /* __gnat_terminate_process - kill a child process
1542 * desc pty_desc structure
1544 int __gnat_terminate_process (pty_desc
*desc
)
1546 return kill (desc
->child_pid
, SIGKILL
);
1549 /* __gnat_terminate_pid - kill a process
1552 * pid unix process id
1555 __gnat_terminate_pid (int pid
)
1557 return kill (pid
, SIGKILL
);
1560 /* __gnat_tty_waitpid - wait for the child process to die
1563 * desc pty_desc structure
1565 * exit status of the child process
1568 __gnat_tty_waitpid (pty_desc
*desc
, int blocking
)
1578 waitpid (desc
->child_pid
, &status
, options
);
1579 if WIFEXITED (status
) {
1580 status
= WEXITSTATUS (status
);
1585 /* __gnat_tty_supported - Are tty supported ?
1588 * always 1 on Unix systems
1591 __gnat_tty_supported (void)
1596 /* __gnat_free_process - free a pty_desc structure
1599 * in out desc: a pty desc structure
1602 __gnat_free_process (pty_desc
** desc
)
1608 /* __gnat_send_header - dummy function. this interface is only used on Windows */
1610 __gnat_send_header (pty_desc
* desc ATTRIBUTE_UNUSED
,
1611 char header
[5] ATTRIBUTE_UNUSED
,
1612 int size ATTRIBUTE_UNUSED
,
1613 int *ret ATTRIBUTE_UNUSED
)
1618 /* __gnat_reset_tty - reset line setting
1621 * desc: a pty_desc structure
1624 __gnat_reset_tty (pty_desc
* desc
)
1626 child_setup_tty (desc
->master_fd
);
1629 /* __gnat_new_tty - allocate a new terminal
1632 * a pty_desc structure
1635 __gnat_new_tty (void)
1638 pty_desc
* desc
= NULL
;
1639 if ((status
= allocate_pty_desc (&desc
)))
1640 child_setup_tty (desc
->master_fd
);
1644 /* __gnat_close_tty - close a terminal
1647 * desc a pty_desc strucure
1649 void __gnat_close_tty (pty_desc
* desc
)
1651 if (desc
->master_fd
>= 0) { close (desc
->master_fd
); desc
->master_fd
= -1; }
1652 if (desc
->slave_fd
>= 0) { close (desc
->slave_fd
); desc
->slave_fd
= -1; }
1655 /* __gnat_tty_name - return slave side device name
1658 * desc a pty_desc strucure
1663 __gnat_tty_name (pty_desc
* desc
)
1665 return desc
->slave_name
;
1668 /* __gnat_tty_name - return master side fd
1671 * desc a pty_desc strucure
1676 __gnat_tty_fd (pty_desc
* desc
)
1678 return desc
->master_fd
;