1 /****************************************************************************
3 * GNAT RUN-TIME COMPONENTS *
7 * C Implementation File *
9 * Copyright (C) 2008-2018, 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 /* First all usupported platforms. Add stubs for exported routines. */
34 #if defined (VMS) || defined (__vxworks) || defined (__Lynx__) \
35 || defined (__ANDROID__) || defined (__PikeOS__) || defined(__DJGPP__)
37 #define ATTRIBUTE_UNUSED __attribute__((unused))
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
)
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)
156 #define MAXPATHLEN 1024
158 #define NILP(x) ((x) == 0)
160 #define report_file_error(x, y) fprintf (stderr, "Error: %s\n", x);
161 #define INTEGERP(x) 1
165 int pid
; /* Number of this process */
166 PROCESS_INFORMATION procinfo
;
167 HANDLE w_infd
, w_outfd
;
168 HANDLE w_forkin
, w_forkout
;
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;
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;
184 static DWORD
AbsoluteSeek(HANDLE
, DWORD
);
185 static VOID
ReadBytes(HANDLE
, LPVOID
, DWORD
);
187 #define XFER_BUFFER_SIZE 2048
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 */
192 is_gui_app (char *exe
)
199 DWORD CoffHeaderOffset
;
200 DWORD MoreDosHeader
[16];
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
;
212 * Open the reference file.
220 file
= malloc ((nlen
+ 1) * sizeof (char));
221 memcpy (file
, &exe
[1], nlen
);
225 hImage
= CreateFile(file
,
230 FILE_ATTRIBUTE_NORMAL
,
237 if (INVALID_HANDLE_VALUE
== hImage
)
239 report_file_error ("Could not open exe: ", Qnil
);
240 report_file_error (exe
, Qnil
);
241 report_file_error ("\n", Qnil
);
242 CloseHandle (hImage
);
247 * Read the MS-DOS image header.
249 ReadBytes(hImage
, &image_dos_header
, sizeof(IMAGE_DOS_HEADER
));
251 if (IMAGE_DOS_SIGNATURE
!= image_dos_header
.e_magic
)
253 report_file_error("Sorry, I do not understand this file.\n", Qnil
);
254 CloseHandle (hImage
);
259 * Read more MS-DOS header. */
260 ReadBytes(hImage
, MoreDosHeader
, sizeof(MoreDosHeader
));
262 * Get actual COFF header.
264 CoffHeaderOffset
= AbsoluteSeek(hImage
, image_dos_header
.e_lfanew
) +
266 if (CoffHeaderOffset
< 0) {
267 CloseHandle (hImage
);
271 ReadBytes (hImage
, &ntSignature
, sizeof(ULONG
));
273 if (IMAGE_NT_SIGNATURE
!= ntSignature
)
275 report_file_error ("Missing NT signature. Unknown file type.\n", Qnil
);
276 CloseHandle (hImage
);
280 SectionOffset
= CoffHeaderOffset
+ IMAGE_SIZEOF_FILE_HEADER
+
281 IMAGE_SIZEOF_NT_OPTIONAL_HEADER
;
283 ReadBytes(hImage
, &image_file_header
, IMAGE_SIZEOF_FILE_HEADER
);
286 * Read optional header.
289 &image_optional_header
,
290 IMAGE_SIZEOF_NT_OPTIONAL_HEADER
);
292 CloseHandle (hImage
);
294 switch (image_optional_header
.Subsystem
)
296 case IMAGE_SUBSYSTEM_UNKNOWN
:
299 case IMAGE_SUBSYSTEM_NATIVE
:
302 case IMAGE_SUBSYSTEM_WINDOWS_GUI
:
305 case IMAGE_SUBSYSTEM_WINDOWS_CUI
:
308 case IMAGE_SUBSYSTEM_OS2_CUI
:
311 case IMAGE_SUBSYSTEM_POSIX_CUI
:
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 */
324 AbsoluteSeek (HANDLE hFile
, DWORD offset
)
328 newOffset
= SetFilePointer (hFile
, offset
, NULL
, FILE_BEGIN
);
330 if (newOffset
== 0xFFFFFFFF)
337 ReadBytes (HANDLE hFile
, LPVOID buffer
, DWORD size
)
341 if (!ReadFile(hFile
, buffer
, size
, &bytes
, NULL
))
346 else if (size
!= bytes
)
353 nt_spawnve (char *exe
, char **argv
, char *env
, struct TTY_Process
*process
)
356 SECURITY_ATTRIBUTES sec_attrs
;
357 SECURITY_DESCRIPTOR sec_desc
;
359 char dir
[ MAXPATHLEN
];
362 char *cmdline
, *parg
, **targ
;
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.
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
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.
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. */
389 if (!NILP (Vw32_quote_process_args
))
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
);
407 int escape_char_run
= 0;
415 /* allow for embedded quotes to be escaped */
418 /* handle the case where the embedded quote is already escaped */
419 if (escape_char_run
> 0)
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
;
427 else if (*p
== ' ' || *p
== '\t')
432 if (*p
== escape_char
&& escape_char
!= '"')
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
;
445 arglen
+= strlen (*targ
) + 1;
449 is_gui
= is_gui_app (argv
[0]);
453 /* could not determine application type. Try launching with "cmd /c" */
459 cmdline
= (char*)malloc (arglen
+ 1);
463 if (use_cmd
== TRUE
) {
464 strcpy (parg
, "cmd /c ");
479 if (*p
== ' ' || *p
== '\t' || *p
== '"')
484 int escape_char_run
= 0;
490 last
= p
+ strlen (p
) - 1;
496 /* double preceding escape chars if any */
497 while (escape_char_run
> 0)
499 *parg
++ = escape_char
;
502 /* escape all quote chars, even at beginning or end */
503 *parg
++ = escape_char
;
507 if (*p
== escape_char
&& escape_char
!= '"')
512 /* double escape chars before enclosing quote */
513 while (escape_char_run
> 0)
515 *parg
++ = escape_char
;
522 strcpy (parg
, *targ
);
523 parg
+= strlen (*targ
);
530 memset (&start
, 0, sizeof (start
));
531 start
.cb
= sizeof (start
);
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
;
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
;
548 /* Explicitly specify no security */
549 if (!InitializeSecurityDescriptor (&sec_desc
, SECURITY_DESCRIPTOR_REVISION
))
551 if (!SetSecurityDescriptorDacl (&sec_desc
, TRUE
, NULL
, FALSE
))
553 sec_attrs
.nLength
= sizeof (sec_attrs
);
554 sec_attrs
.lpSecurityDescriptor
= &sec_desc
;
555 sec_attrs
.bInheritHandle
= FALSE
;
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
;
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
;
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
))
574 pid
= (int) process
->procinfo
.hProcess
;
583 /*************************
584 ** __gnat_send_header ()
585 *************************/
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'
593 #define EXP_KILL_TERMINATE 0x1
594 #define EXP_KILL_CTRL_C 0x2
595 #define EXP_KILL_CTRL_BREAK 0x4
598 __gnat_send_header (struct TTY_Process
* p
, char header
[5], int size
, int *ret
)
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;
612 /**********************************
613 ** __gnat_setup_communication ()
614 **********************************/
617 __gnat_setup_communication (struct TTY_Process
** process_out
) /* output param */
619 struct TTY_Process
* process
;
621 process
= (struct TTY_Process
*)malloc (sizeof (struct TTY_Process
));
622 ZeroMemory (process
, sizeof (struct TTY_Process
));
623 *process_out
= process
;
628 #define EXP_PIPE_BASENAME "\\\\.\\pipe\\ExpectPipe"
631 __gnat_setup_child_communication
632 (struct TTY_Process
* process
,
638 SECURITY_ATTRIBUTES sec_attrs
;
639 char slavePath
[MAX_PATH
];
643 char pipeNameIn
[100];
644 HANDLE hSlaveInDrv
= NULL
; /* Handle to communicate with slave driver */
646 parent
= GetCurrentProcess ();
648 /* Set inheritance for the pipe handles */
649 sec_attrs
.nLength
= sizeof (SECURITY_ATTRIBUTES
);
650 sec_attrs
.bInheritHandle
= TRUE
;
651 sec_attrs
.lpSecurityDescriptor
= NULL
;
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
);
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);
664 /* use native argv */
666 process
->usePipe
= TRUE
;
669 static int pipeNameId
= 0;
671 process
->w_infd
= NULL
;
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
676 sprintf(pipeNameIn
, "%sIn%08x_%08x", EXP_PIPE_BASENAME
,
677 GetCurrentProcessId(), pipeNameId
);
680 hSlaveInDrv
= CreateNamedPipe(pipeNameIn
,
681 PIPE_ACCESS_OUTBOUND
,
682 PIPE_TYPE_BYTE
| PIPE_WAIT
, 1, 8192, 8192,
684 if (hSlaveInDrv
== NULL
) goto end
;
686 if (!CreatePipe (&process
->w_outfd
, &process
->w_forkout
, &sec_attrs
, 0))
687 report_file_error ("Creation of child's OUT handle", Qnil
);
689 if (SearchPath (NULL
, "explaunch.exe", NULL
,
690 MAX_PATH
, slavePath
, NULL
) == 0) goto end
;
692 for (argc
=0; argv
[argc
] != NULL
; argc
++) ;
693 nargv
= (char **) malloc (sizeof (char*) * (argc
+ 3));
694 nargv
[0] = slavePath
;
695 nargv
[1] = pipeNameIn
;
697 for (i
= 0; i
<= argc
; i
++) nargv
[i
+ 2] = argv
[i
];
698 process
->usePipe
= FALSE
;
701 /* Spawn the child. */
702 cpid
= nt_spawnve (nargv
[0], nargv
, NULL
, process
);
704 /* close the duplicated handles passed to the child */
705 CloseHandle (process
->w_forkout
);
707 if (process
->usePipe
== TRUE
) {
708 CloseHandle (process
->w_forkin
);
711 UCHAR buf
[8]; /* enough space for child status info */
717 * Wait for connection with the slave driver
719 bRet
= ConnectNamedPipe(hSlaveInDrv
, NULL
);
721 dwRet
= GetLastError();
722 if (dwRet
== ERROR_PIPE_CONNECTED
) {
729 process
->w_infd
= hSlaveInDrv
;
732 * wait for slave driver to initialize before allowing user to send to it
734 bRet
= ReadFile(process
->w_outfd
, buf
, 8, &count
, NULL
);
739 dwRet
= buf
[0] | (buf
[1] << 8) | (buf
[2] << 16) | (buf
[3] << 24);
744 cpid
= buf
[4] | (buf
[5] << 8) | (buf
[6] << 16) | (buf
[7] << 24);
749 /* An error occurred while trying to spawn the process. */
750 report_file_error ("Spawning child process", Qnil
);
754 if (hSlaveInDrv
!= NULL
)
755 CloseHandle (hSlaveInDrv
);
760 __gnat_setup_parent_communication
761 (struct TTY_Process
* process
,
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 */
774 typedef struct _child_process
777 PROCESS_INFORMATION
*procinfo
;
780 /* The major and minor versions of NT. */
781 static int w32_major_version
;
782 static int w32_minor_version
;
784 /* Distinguish between Windows NT and Windows 95. */
785 static enum {OS_UNKNOWN
, OS_WIN95
, OS_NT
} os_subtype
= OS_UNKNOWN
;
787 /* Cache information describing the NT system for later use. */
789 cache_system_info (void)
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
;
807 if (version
.info
.platform
& 0x8000)
808 os_subtype
= OS_WIN95
;
814 find_child_console (HWND hwnd
, child_process
* cp
)
819 thread_id
= GetWindowThreadProcessId (hwnd
, &process_id
);
820 if (process_id
== cp
->procinfo
->dwProcessId
)
822 char window_class
[32];
824 GetClassName (hwnd
, window_class
, sizeof (window_class
));
825 if (strcmp (window_class
,
826 (os_subtype
== OS_WIN95
)
828 : "ConsoleWindowClass") == 0)
839 __gnat_interrupt_process (struct TTY_Process
* p
)
845 if (p
->usePipe
== TRUE
) {
848 buf
[0] = EXP_SLAVE_KILL
;
849 buf
[1] = EXP_KILL_CTRL_C
;
850 bret
= WriteFile (p
->w_infd
, buf
, 2, &written
, NULL
);
854 return __gnat_interrupt_pid (p
->procinfo
.dwProcessId
);
860 __gnat_interrupt_pid (int pid
)
862 volatile child_process cp
;
865 cp
.procinfo
= (LPPROCESS_INFORMATION
) malloc (sizeof (PROCESS_INFORMATION
));
866 cp
.procinfo
->dwProcessId
= pid
;
868 if (os_subtype
== OS_UNKNOWN
)
869 cache_system_info ();
871 /* Try to locate console window for process. */
872 EnumWindows ((WNDENUMPROC
) find_child_console
, (LPARAM
) &cp
);
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
;
882 foreground_window
= GetForegroundWindow ();
883 if (foreground_window
)
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
;
894 GetWindowThreadProcessId (foreground_window
, NULL
);
895 if (foreground_thread
== GetCurrentThreadId ()
896 || !AttachThreadInput (GetCurrentThreadId (),
897 foreground_thread
, TRUE
))
898 foreground_thread
= 0;
900 child_thread
= GetWindowThreadProcessId (cp
.hwnd
, NULL
);
901 if (child_thread
== GetCurrentThreadId ()
902 || !AttachThreadInput (GetCurrentThreadId (),
906 /* Set the foreground window to the child. */
907 if (SetForegroundWindow (cp
.hwnd
))
909 /* Generate keystrokes as if user had typed Ctrl-Break or
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);
919 /* Sleep for a bit to give time for the main frame to respond
920 to focus change events. */
923 SetForegroundWindow (foreground_window
);
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
);
930 AttachThreadInput (GetCurrentThreadId (), child_thread
, FALSE
);
933 /* Ctrl-Break is NT equivalent of SIGINT. */
934 else if (!GenerateConsoleCtrlEvent
935 (CTRL_BREAK_EVENT
, cp
.procinfo
->dwProcessId
))
945 /* kill a process, as this implementation use CreateProcess on Win32 we need
946 to use Win32 TerminateProcess API */
948 __gnat_terminate_process (struct TTY_Process
* p
)
954 if (p
->usePipe
== TRUE
) {
957 buf
[0] = EXP_SLAVE_KILL
;
958 buf
[1] = EXP_KILL_TERMINATE
;
959 bret
= WriteFile (p
->w_infd
, buf
, 2, &written
, NULL
);
963 if (!TerminateProcess (p
->procinfo
.hProcess
, 1))
977 find_process_handle (HWND hwnd
, pid_struct
* ps
)
982 thread_id
= GetWindowThreadProcessId (hwnd
, &process_id
);
983 if (process_id
== ps
->dwProcessId
)
993 __gnat_terminate_pid (int pid
)
997 ps
.dwProcessId
= pid
;
999 EnumWindows ((WNDENUMPROC
) find_process_handle
, (LPARAM
) &ps
);
1003 if (!TerminateProcess (ps
.hwnd
, 1))
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. */
1017 __gnat_tty_waitpid (struct TTY_Process
* p
)
1021 HANDLE proc_hand
= p
->procinfo
.hProcess
;
1023 res
= WaitForSingleObject (proc_hand
, 0);
1024 GetExitCodeProcess (proc_hand
, &exitcode
);
1026 CloseHandle (p
->procinfo
.hThread
);
1027 CloseHandle (p
->procinfo
.hProcess
);
1029 /* No need to close the handles: they were closed on the ada side */
1031 return (int) exitcode
;
1034 /********************************
1035 ** __gnat_free_process ()
1036 ********************************/
1039 __gnat_free_process (struct TTY_Process
** process
)
1048 int tty_fd
; /* descriptor for the tty */
1049 char tty_name
[24]; /* Name of TTY device */
1053 __gnat_tty_supported (void)
1058 /* Return the tty name associated with p */
1061 __gnat_tty_name (TTY_Handle
* t
)
1067 __gnat_tty_fd (TTY_Handle
* t
)
1073 __gnat_new_tty (void)
1075 return (TTY_Handle
*)0;
1079 __gnat_reset_tty (TTY_Handle
* t
)
1085 __gnat_close_tty (TTY_Handle
* t
)
1091 __gnat_setup_winsize (void *desc
, int rows
, int columns
)
1095 #else /* defined(_WIN32, implementatin for all UNIXes */
1097 /* First defined some macro to identify easily some systems */
1098 #if defined (__FreeBSD__) \
1099 || defined (__OpenBSD__) \
1100 || defined (__NetBSD__) \
1101 || defined (__DragonFly__)
1105 /* Include every system header we need */
1110 #include <sys/ioctl.h>
1111 #include <termios.h>
1114 #include <sys/stat.h>
1115 #include <sys/types.h>
1116 #include <sys/wait.h>
1118 #if defined (__sun__)
1119 # include <sys/stropts.h>
1121 #if defined (BSD) || defined (__sun__)
1122 # include <sys/signal.h>
1124 #if defined (__hpux__)
1125 # include <sys/stropts.h>
1128 #define CDISABLE _POSIX_VDISABLE
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)
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)
1142 When using the cloning device method, the macro USE_CLONE_DEVICE should
1143 contains a full path to the adequate device.
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
1149 /* Configurable part */
1150 #if defined (__APPLE__) || defined (BSD)
1152 #elif defined (__linux__)
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"
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
1175 /* allocate_pty_desc - allocate a pseudo terminal
1178 * out desc returned pointer to a pty_desc structure containing information
1179 * about the opened pseudo terminal
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
1191 extern char* ptsname (int);
1194 allocate_pty_desc (pty_desc
**desc
) {
1200 char *slave_name
= NULL
;
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);
1209 printf ("[error]: terminal support is not configured\n");
1213 /* at this stage we should have the master side fd and status should be 0 */
1214 if (status
!= 0 || master_fd
< 0)
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
);
1224 /* retrieve the file name of the slave side if necessary */
1225 if (slave_name
== NULL
) slave_name
= (char *) ptsname (master_fd
);
1227 /* Now we should have slave file name */
1228 if (slave_name
== NULL
)
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
);
1238 #if !defined(__rtems__)
1239 /* grant access to the slave side */
1240 grantpt (master_fd
);
1241 /* unlock the terminal */
1242 unlockpt (master_fd
);
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;
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))
1262 /* some properties do not exist on all systems. Set their value to 0 in that
1279 /* child_setup_tty - set terminal properties
1282 * file descriptor of the slave side of the terminal
1285 * 0 if success, any other value if failed.
1291 child_setup_tty (int fd
)
1296 /* ensure that s is filled with 0 */
1297 bzero (&s
, sizeof (s
));
1299 /* Get the current terminal settings */
1300 status
= tcgetattr (fd
, &s
);
1301 if (status
!= 0) return -1;
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 */
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 */
1314 /* Adjust control modes */
1315 s
.c_cflag
= (s
.c_cflag
& ~CSIZE
) | CS8
; /* Don't strip 8th bit */
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 */
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 */
1333 /* push our changes */
1334 status
= tcsetattr (fd
, TCSADRAIN
, &s
);
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
1344 * out desc returned pointer to a pty_desc structure
1346 * 0 if success, -1 otherwise
1348 int __gnat_setup_communication (pty_desc
** desc
) {
1349 return allocate_pty_desc (desc
);
1352 /* __gnat_setup_parent_communication - interface to the external world. Should
1353 * be called after forking in the parent process
1358 out err_fd fds corresponding to the parent side of the
1360 in pid_out child process pid
1365 __gnat_setup_parent_communication
1367 int* in_fd
, /* input */
1368 int* out_fd
, /* output */
1369 int* err_fd
, /* error */
1373 *in_fd
= desc
->master_fd
;
1374 *out_fd
= desc
->master_fd
;
1375 *err_fd
= desc
->master_fd
;
1376 desc
->child_pid
= *pid_out
;
1379 /* __gnat_setup_winsize - Sets up the size of the terminal
1380 * This lets the process know the size of the terminal
1383 void __gnat_setup_winsize (pty_desc
*desc
, int rows
, int columns
) {
1386 s
.ws_row
= (unsigned short)rows
;
1387 s
.ws_col
= (unsigned short)columns
;
1390 ioctl (desc
->master_fd
, TIOCSWINSZ
, &s
);
1392 if (desc
->child_pid
> 0) {
1393 /* Let the process know about the change in size */
1394 kill (desc
->child_pid
, SIGWINCH
);
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.
1406 * desc a pty_desc structure containing the pty parameters
1407 * new_argv argv of the program to be spawned
1409 * this function should not return
1412 __gnat_setup_child_communication
1418 int pid
= getpid ();
1422 /* open the slave side of the terminal if necessary */
1423 if (desc
->slave_fd
== -1)
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);
1437 desc
->slave_fd
= open (desc
->slave_name
, O_RDWR
, 0);
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");
1450 /* make the tty the controlling terminal */
1451 if ((status
= ioctl (desc
->slave_fd
, TIOCSCTTY
, 0)) == -1)
1455 /* adjust tty settings */
1456 child_setup_tty (desc
->slave_fd
);
1457 __gnat_setup_winsize (desc
, 24, 80); /* To prevent errors in some shells */
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
);
1465 /* adjust process group settings */
1466 /* ignore failures of the following two commands as the context might not
1467 * allow making those changes. */
1471 /* launch the program */
1472 execvp (new_argv
[0], new_argv
);
1477 /* send_signal_via_characters - Send a characters that will trigger a signal
1478 * in the child process.
1481 * desc a pty_desc structure containing terminal information
1482 * int a signal number
1487 send_signal_via_characters
1492 char ctrl_backslash
= 28;
1495 switch (signal_number
)
1498 write (desc
->master_fd
, &ctrl_c
, 1); return;
1500 write (desc
->master_fd
, &ctrl_backslash
, 1); return;
1502 write (desc
->master_fd
, &ctrl_Z
, 1); return;
1506 /* __gnat_interrupt_process - interrupt the child process
1509 * desc a pty_desc structure
1512 __gnat_interrupt_process (pty_desc
*desc
)
1514 send_signal_via_characters (desc
, SIGINT
);
1518 /* __gnat_interrupt_pid - interrupt a process group
1521 * pid pid of the process to interrupt
1524 __gnat_interrupt_pid (int pid
)
1526 kill (-pid
, SIGINT
);
1530 /* __gnat_terminate_process - kill a child process
1533 * desc pty_desc structure
1535 int __gnat_terminate_process (pty_desc
*desc
)
1537 return kill (desc
->child_pid
, SIGKILL
);
1540 /* __gnat_terminate_pid - kill a process
1543 * pid unix process id
1546 __gnat_terminate_pid (int pid
)
1548 return kill (pid
, SIGKILL
);
1551 /* __gnat_tty_waitpid - wait for the child process to die
1554 * desc pty_desc structure
1556 * exit status of the child process
1559 __gnat_tty_waitpid (pty_desc
*desc
)
1562 waitpid (desc
->child_pid
, &status
, 0);
1563 return WEXITSTATUS (status
);
1566 /* __gnat_tty_supported - Are tty supported ?
1569 * always 1 on Unix systems
1572 __gnat_tty_supported (void)
1577 /* __gnat_free_process - free a pty_desc structure
1580 * in out desc: a pty desc structure
1583 __gnat_free_process (pty_desc
** desc
)
1589 /* __gnat_send_header - dummy function. this interface is only used on Windows */
1591 __gnat_send_header (pty_desc
* desc
, char header
[5], int size
, int *ret
)
1596 /* __gnat_reset_tty - reset line setting
1599 * desc: a pty_desc structure
1602 __gnat_reset_tty (pty_desc
* desc
)
1604 child_setup_tty (desc
->master_fd
);
1607 /* __gnat_new_tty - allocate a new terminal
1610 * a pty_desc structure
1613 __gnat_new_tty (void)
1616 pty_desc
* desc
= NULL
;
1617 if ((status
= allocate_pty_desc (&desc
)))
1618 child_setup_tty (desc
->master_fd
);
1622 /* __gnat_close_tty - close a terminal
1625 * desc a pty_desc strucure
1627 void __gnat_close_tty (pty_desc
* desc
)
1629 if (desc
->master_fd
>= 0) close (desc
->master_fd
);
1630 if (desc
->slave_fd
>= 0) close (desc
->slave_fd
);
1633 /* __gnat_tty_name - return slave side device name
1636 * desc a pty_desc strucure
1641 __gnat_tty_name (pty_desc
* desc
)
1643 return desc
->slave_name
;
1646 /* __gnat_tty_name - return master side fd
1649 * desc a pty_desc strucure
1654 __gnat_tty_fd (pty_desc
* desc
)
1656 return desc
->master_fd
;