1 /****************************************************************************
3 * GNAT RUN-TIME COMPONENTS *
7 * C Implementation File *
9 * Copyright (C) 2008-2012, 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__)
36 void * __gnat_new_tty (void) { return (void*)0; }
37 char * __gnat_tty_name (void* t
) { return (char*)0; }
38 int __gnat_interrupt_pid (int pid
) { return -1; }
39 int __gnat_interrupt_process (void* desc
) { return -1; }
40 int __gnat_setup_communication (void** desc
) { return -1; }
41 void __gnat_setup_parent_communication
42 (void* d
, int* i
, int* o
, int*e
, int*p
) { return -1; }
43 int __gnat_setup_child_communication
44 (void* d
, char **n
, int u
) { return -1; }
45 int __gnat_terminate_process (void *desc
) { return -1; }
46 int __gnat_tty_fd (void* t
) { return -1; }
47 int __gnat_tty_supported (void) { return 0; }
48 int __gnat_tty_waitpid (void *desc
) { return 1; }
49 void __gnat_close_tty (void* t
) {}
50 void __gnat_free_process (void** process
) {}
51 void __gnat_reset_tty (void* t
) {}
52 void __gnat_send_header (void* d
, char h
[5], int s
, int *r
) {}
53 void __gnat_setup_winsize (void *desc
, int rows
, int columns
) {}
55 /* For Windows platforms. */
65 #define MAXPATHLEN 1024
67 #define NILP(x) ((x) == 0)
69 #define report_file_error(x, y) fprintf (stderr, "Error: %s\n", x);
74 int pid
; /* Number of this process */
75 PROCESS_INFORMATION procinfo
;
76 HANDLE w_infd
, w_outfd
;
77 HANDLE w_forkin
, w_forkout
;
81 /* Control whether create_child cause the process to inherit GPS'
82 error mode setting. The default is 1, to minimize the possibility of
83 subprocesses blocking when accessing unmounted drives. */
84 static int Vw32_start_process_inherit_error_mode
= 1;
86 /* Control whether spawnve quotes arguments as necessary to ensure
87 correct parsing by child process. Because not all uses of spawnve
88 are careful about constructing argv arrays, we make this behaviour
89 conditional (off by default, since a similar operation is already done
90 in g-expect.adb by calling Normalize_Argument). */
91 static int Vw32_quote_process_args
= 0;
93 static DWORD
AbsoluteSeek(HANDLE
, DWORD
);
94 static VOID
ReadBytes(HANDLE
, LPVOID
, DWORD
);
96 #define XFER_BUFFER_SIZE 2048
98 /* This tell if the executable we're about to launch uses a GUI interface. */
99 /* if we can't determine it, we will return true */
101 is_gui_app (char *exe
)
108 DWORD CoffHeaderOffset
;
109 DWORD MoreDosHeader
[16];
115 IMAGE_DOS_HEADER image_dos_header
;
116 IMAGE_FILE_HEADER image_file_header
;
117 IMAGE_OPTIONAL_HEADER image_optional_header
;
118 IMAGE_SECTION_HEADER image_section_header
;
121 * Open the reference file.
129 file
= malloc ((nlen
+ 1) * sizeof (char));
130 memcpy (file
, &exe
[1], nlen
);
134 hImage
= CreateFile(file
,
139 FILE_ATTRIBUTE_NORMAL
,
146 if (INVALID_HANDLE_VALUE
== hImage
)
148 report_file_error ("Could not open exe: ", Qnil
);
149 report_file_error (exe
, Qnil
);
150 report_file_error ("\n", Qnil
);
151 CloseHandle (hImage
);
156 * Read the MS-DOS image header.
158 ReadBytes(hImage
, &image_dos_header
, sizeof(IMAGE_DOS_HEADER
));
160 if (IMAGE_DOS_SIGNATURE
!= image_dos_header
.e_magic
)
162 report_file_error("Sorry, I do not understand this file.\n", Qnil
);
163 CloseHandle (hImage
);
168 * Read more MS-DOS header. */
169 ReadBytes(hImage
, MoreDosHeader
, sizeof(MoreDosHeader
));
171 * Get actual COFF header.
173 CoffHeaderOffset
= AbsoluteSeek(hImage
, image_dos_header
.e_lfanew
) +
175 if (CoffHeaderOffset
< 0) {
176 CloseHandle (hImage
);
180 ReadBytes (hImage
, &ntSignature
, sizeof(ULONG
));
182 if (IMAGE_NT_SIGNATURE
!= ntSignature
)
184 report_file_error ("Missing NT signature. Unknown file type.\n", Qnil
);
185 CloseHandle (hImage
);
189 SectionOffset
= CoffHeaderOffset
+ IMAGE_SIZEOF_FILE_HEADER
+
190 IMAGE_SIZEOF_NT_OPTIONAL_HEADER
;
192 ReadBytes(hImage
, &image_file_header
, IMAGE_SIZEOF_FILE_HEADER
);
195 * Read optional header.
198 &image_optional_header
,
199 IMAGE_SIZEOF_NT_OPTIONAL_HEADER
);
201 CloseHandle (hImage
);
203 switch (image_optional_header
.Subsystem
)
205 case IMAGE_SUBSYSTEM_UNKNOWN
:
209 case IMAGE_SUBSYSTEM_NATIVE
:
213 case IMAGE_SUBSYSTEM_WINDOWS_GUI
:
217 case IMAGE_SUBSYSTEM_WINDOWS_CUI
:
221 case IMAGE_SUBSYSTEM_OS2_CUI
:
225 case IMAGE_SUBSYSTEM_POSIX_CUI
:
230 /* Unknown, return GUI app to be preservative: if yes, it will be
231 correctly launched, if no, it will be launched, and a console will
232 be also displayed, which is not a big deal */
240 AbsoluteSeek (HANDLE hFile
, DWORD offset
)
244 newOffset
= SetFilePointer (hFile
, offset
, NULL
, FILE_BEGIN
);
246 if (newOffset
== 0xFFFFFFFF)
253 ReadBytes (HANDLE hFile
, LPVOID buffer
, DWORD size
)
257 if (!ReadFile(hFile
, buffer
, size
, &bytes
, NULL
))
262 else if (size
!= bytes
)
269 nt_spawnve (char *exe
, char **argv
, char *env
, struct TTY_Process
*process
)
272 SECURITY_ATTRIBUTES sec_attrs
;
273 SECURITY_DESCRIPTOR sec_desc
;
275 char dir
[ MAXPATHLEN
];
278 char *cmdline
, *parg
, **targ
;
283 /* we have to do some conjuring here to put argv and envp into the
284 form CreateProcess wants... argv needs to be a space separated/null
285 terminated list of parameters, and envp is a null
286 separated/double-null terminated list of parameters.
288 Additionally, zero-length args and args containing whitespace or
289 quote chars need to be wrapped in double quotes - for this to work,
290 embedded quotes need to be escaped as well. The aim is to ensure
291 the child process reconstructs the argv array we start with
292 exactly, so we treat quotes at the beginning and end of arguments
295 Note that using backslash to escape embedded quotes requires
296 additional special handling if an embedded quote is already
297 preceeded by backslash, or if an arg requiring quoting ends with
298 backslash. In such cases, the run of escape characters needs to be
299 doubled. For consistency, we apply this special handling as long
300 as the escape character is not quote.
302 Since we have no idea how large argv and envp are likely to be we
303 figure out list lengths on the fly and allocate them. */
305 if (!NILP (Vw32_quote_process_args
))
308 /* Override escape char by binding w32-quote-process-args to
309 desired character, or use t for auto-selection. */
310 if (INTEGERP (Vw32_quote_process_args
))
311 escape_char
= XINT (Vw32_quote_process_args
);
323 int escape_char_run
= 0;
331 /* allow for embedded quotes to be escaped */
334 /* handle the case where the embedded quote is already escaped */
335 if (escape_char_run
> 0)
337 /* To preserve the arg exactly, we need to double the
338 preceding escape characters (plus adding one to
339 escape the quote character itself). */
340 arglen
+= escape_char_run
;
343 else if (*p
== ' ' || *p
== '\t')
348 if (*p
== escape_char
&& escape_char
!= '"')
356 /* handle the case where the arg ends with an escape char - we
357 must not let the enclosing quote be escaped. */
358 if (escape_char_run
> 0)
359 arglen
+= escape_char_run
;
361 arglen
+= strlen (*targ
) + 1;
365 is_gui
= is_gui_app (argv
[0]);
369 /* could not determine application type. Try launching with "cmd /c" */
375 cmdline
= (char*)malloc (arglen
+ 1);
379 if (use_cmd
== TRUE
) {
380 strcpy (parg
, "cmd /c ");
395 if (*p
== ' ' || *p
== '\t' || *p
== '"')
400 int escape_char_run
= 0;
406 last
= p
+ strlen (p
) - 1;
412 /* double preceding escape chars if any */
413 while (escape_char_run
> 0)
415 *parg
++ = escape_char
;
418 /* escape all quote chars, even at beginning or end */
419 *parg
++ = escape_char
;
423 if (*p
== escape_char
&& escape_char
!= '"')
428 /* double escape chars before enclosing quote */
429 while (escape_char_run
> 0)
431 *parg
++ = escape_char
;
438 strcpy (parg
, *targ
);
439 parg
+= strlen (*targ
);
446 memset (&start
, 0, sizeof (start
));
447 start
.cb
= sizeof (start
);
449 if (process
->usePipe
== TRUE
) {
450 start
.dwFlags
= STARTF_USESTDHANDLES
;
451 start
.hStdInput
= process
->w_forkin
;
452 start
.hStdOutput
= process
->w_forkout
;
453 /* child's stderr is always redirected to outfd */
454 start
.hStdError
= process
->w_forkout
;
456 start
.dwFlags
= STARTF_USESTDHANDLES
;
457 /* We only need to redirect stderr/stdout here. Stdin will be forced to
458 the spawned process console by explaunch */
459 start
.hStdInput
= NULL
;
460 start
.hStdOutput
= process
->w_forkout
;
461 start
.hStdError
= process
->w_forkout
;
464 /* Explicitly specify no security */
465 if (!InitializeSecurityDescriptor (&sec_desc
, SECURITY_DESCRIPTOR_REVISION
))
467 if (!SetSecurityDescriptorDacl (&sec_desc
, TRUE
, NULL
, FALSE
))
469 sec_attrs
.nLength
= sizeof (sec_attrs
);
470 sec_attrs
.lpSecurityDescriptor
= &sec_desc
;
471 sec_attrs
.bInheritHandle
= FALSE
;
473 /* creating a new console allow easier close. Do not use
474 CREATE_NEW_PROCESS_GROUP as this results in disabling Ctrl+C */
475 flags
= CREATE_NEW_CONSOLE
;
476 if (NILP (Vw32_start_process_inherit_error_mode
))
477 flags
|= CREATE_DEFAULT_ERROR_MODE
;
479 /* if app is not a gui application, hide the console */
480 if (is_gui
== FALSE
) {
481 start
.dwFlags
|= STARTF_USESHOWWINDOW
;
482 start
.wShowWindow
= SW_HIDE
;
485 /* Set initial directory to null character to use current directory */
486 if (!CreateProcess (NULL
, cmdline
, &sec_attrs
, NULL
, TRUE
,
487 flags
, env
, NULL
, &start
, &process
->procinfo
))
490 pid
= (int) process
->procinfo
.hProcess
;
499 /*************************
500 ** __gnat_send_header ()
501 *************************/
503 #define EXP_SLAVE_CREATE 'c'
504 #define EXP_SLAVE_KEY 'k'
505 #define EXP_SLAVE_MOUSE 'm'
506 #define EXP_SLAVE_WRITE 'w'
507 #define EXP_SLAVE_KILL 'x'
509 #define EXP_KILL_TERMINATE 0x1
510 #define EXP_KILL_CTRL_C 0x2
511 #define EXP_KILL_CTRL_BREAK 0x4
514 __gnat_send_header (struct TTY_Process
* p
, char header
[5], int size
, int *ret
)
516 if (p
->usePipe
== FALSE
) {
517 header
[0] = EXP_SLAVE_WRITE
;
518 header
[1] = size
& 0xff;
519 header
[2] = (size
& 0xff00) >> 8;
520 header
[3] = (size
& 0xff0000) >> 16;
521 header
[4] = (size
& 0xff000000) >> 24;
528 /**********************************
529 ** __gnat_setup_communication ()
530 **********************************/
533 __gnat_setup_communication (struct TTY_Process
** process_out
) /* output param */
535 struct TTY_Process
* process
;
537 process
= (struct TTY_Process
*)malloc (sizeof (struct TTY_Process
));
538 ZeroMemory (process
, sizeof (struct TTY_Process
));
539 *process_out
= process
;
544 #define EXP_PIPE_BASENAME "\\\\.\\pipe\\ExpectPipe"
547 __gnat_setup_child_communication
548 (struct TTY_Process
* process
,
554 SECURITY_ATTRIBUTES sec_attrs
;
555 char slavePath
[MAX_PATH
];
559 char pipeNameIn
[100];
560 HANDLE hSlaveInDrv
= NULL
; /* Handle to communicate with slave driver */
562 parent
= GetCurrentProcess ();
564 /* Set inheritance for the pipe handles */
565 sec_attrs
.nLength
= sizeof (SECURITY_ATTRIBUTES
);
566 sec_attrs
.bInheritHandle
= TRUE
;
567 sec_attrs
.lpSecurityDescriptor
= NULL
;
570 /* Create in and out pipes */
571 if (!CreatePipe (&process
->w_forkin
, &process
->w_infd
, &sec_attrs
, 0))
572 report_file_error ("Creation of child's IN handle", Qnil
);
573 if (!CreatePipe (&process
->w_outfd
, &process
->w_forkout
, &sec_attrs
, 0))
574 report_file_error ("Creation of child's OUT handle", Qnil
);
576 /* Do not inherit the parent's side of the pipes */
577 SetHandleInformation (&process
->w_infd
, HANDLE_FLAG_INHERIT
, 0);
578 SetHandleInformation (&process
->w_outfd
, HANDLE_FLAG_INHERIT
, 0);
580 /* use native argv */
582 process
->usePipe
= TRUE
;
585 static int pipeNameId
= 0;
587 process
->w_infd
= NULL
;
589 /* We create a named pipe for Input, as we handle input by sending special
590 commands to the explaunch process, that uses it to feed the actual input
592 sprintf(pipeNameIn
, "%sIn%08x_%08x", EXP_PIPE_BASENAME
,
593 GetCurrentProcessId(), pipeNameId
);
596 hSlaveInDrv
= CreateNamedPipe(pipeNameIn
,
597 PIPE_ACCESS_OUTBOUND
,
598 PIPE_TYPE_BYTE
| PIPE_WAIT
, 1, 8192, 8192,
600 if (hSlaveInDrv
== NULL
) goto end
;
602 if (!CreatePipe (&process
->w_outfd
, &process
->w_forkout
, &sec_attrs
, 0))
603 report_file_error ("Creation of child's OUT handle", Qnil
);
605 if (SearchPath (NULL
, "explaunch.exe", NULL
,
606 MAX_PATH
, slavePath
, NULL
) == 0) goto end
;
608 for (argc
=0; argv
[argc
] != NULL
; argc
++) ;
609 nargv
= (char **) malloc (sizeof (char*) * (argc
+ 3));
610 nargv
[0] = slavePath
;
611 nargv
[1] = pipeNameIn
;
613 for (i
= 0; i
<= argc
; i
++) nargv
[i
+ 2] = argv
[i
];
614 process
->usePipe
= FALSE
;
617 /* Spawn the child. */
618 cpid
= nt_spawnve (nargv
[0], nargv
, NULL
, process
);
620 /* close the duplicated handles passed to the child */
621 CloseHandle (process
->w_forkout
);
623 if (process
->usePipe
== TRUE
) {
624 CloseHandle (process
->w_forkin
);
627 UCHAR buf
[8]; /* enough space for child status info */
633 * Wait for connection with the slave driver
635 bRet
= ConnectNamedPipe(hSlaveInDrv
, NULL
);
637 dwRet
= GetLastError();
638 if (dwRet
== ERROR_PIPE_CONNECTED
) {
645 process
->w_infd
= hSlaveInDrv
;
648 * wait for slave driver to initialize before allowing user to send to it
650 bRet
= ReadFile(process
->w_outfd
, buf
, 8, &count
, NULL
);
655 dwRet
= buf
[0] | (buf
[1] << 8) | (buf
[2] << 16) | (buf
[3] << 24);
660 cpid
= buf
[4] | (buf
[5] << 8) | (buf
[6] << 16) | (buf
[7] << 24);
665 /* An error occurred while trying to spawn the process. */
666 report_file_error ("Spawning child process", Qnil
);
670 if (hSlaveInDrv
!= NULL
)
671 CloseHandle (hSlaveInDrv
);
676 __gnat_setup_parent_communication
677 (struct TTY_Process
* process
,
683 *in
= _open_osfhandle ((long) process
->w_infd
, 0);
684 *out
= _open_osfhandle ((long) process
->w_outfd
, 0);
685 /* child's stderr is always redirected to outfd */
690 typedef struct _child_process
693 PROCESS_INFORMATION
*procinfo
;
696 /* The major and minor versions of NT. */
697 static int w32_major_version
;
698 static int w32_minor_version
;
700 /* Distinguish between Windows NT and Windows 95. */
701 static enum {OS_UNKNOWN
, OS_WIN95
, OS_NT
} os_subtype
= OS_UNKNOWN
;
703 /* Cache information describing the NT system for later use. */
705 cache_system_info (void)
718 /* Cache the version of the operating system. */
719 version
.data
= GetVersion ();
720 w32_major_version
= version
.info
.major
;
721 w32_minor_version
= version
.info
.minor
;
723 if (version
.info
.platform
& 0x8000)
724 os_subtype
= OS_WIN95
;
730 find_child_console (HWND hwnd
, child_process
* cp
)
735 thread_id
= GetWindowThreadProcessId (hwnd
, &process_id
);
736 if (process_id
== cp
->procinfo
->dwProcessId
)
738 char window_class
[32];
740 GetClassName (hwnd
, window_class
, sizeof (window_class
));
741 if (strcmp (window_class
,
742 (os_subtype
== OS_WIN95
)
744 : "ConsoleWindowClass") == 0)
755 __gnat_interrupt_process (struct TTY_Process
* p
)
761 if (p
->usePipe
== TRUE
) {
764 buf
[0] = EXP_SLAVE_KILL
;
765 buf
[1] = EXP_KILL_CTRL_C
;
766 bret
= WriteFile (p
->w_infd
, buf
, 2, &written
, NULL
);
770 return __gnat_interrupt_pid (p
->procinfo
.dwProcessId
);
776 __gnat_interrupt_pid (int pid
)
778 volatile child_process cp
;
781 cp
.procinfo
= (LPPROCESS_INFORMATION
) malloc (sizeof (PROCESS_INFORMATION
));
782 cp
.procinfo
->dwProcessId
= pid
;
784 if (os_subtype
== OS_UNKNOWN
)
785 cache_system_info ();
787 /* Try to locate console window for process. */
788 EnumWindows ((WNDENUMPROC
) find_child_console
, (LPARAM
) &cp
);
792 BYTE control_scan_code
= (BYTE
) MapVirtualKey (VK_CONTROL
, 0);
793 /* Retrieve Ctrl-C scancode */
794 BYTE vk_break_code
= 'C';
795 BYTE break_scan_code
= (BYTE
) MapVirtualKey (vk_break_code
, 0);
796 HWND foreground_window
;
798 foreground_window
= GetForegroundWindow ();
799 if (foreground_window
)
801 /* NT 5.0, and apparently also Windows 98, will not allow
802 a Window to be set to foreground directly without the
803 user's involvement. The workaround is to attach
804 ourselves to the thread that owns the foreground
805 window, since that is the only thread that can set the
806 foreground window. */
807 DWORD foreground_thread
, child_thread
;
810 GetWindowThreadProcessId (foreground_window
, NULL
);
811 if (foreground_thread
== GetCurrentThreadId ()
812 || !AttachThreadInput (GetCurrentThreadId (),
813 foreground_thread
, TRUE
))
814 foreground_thread
= 0;
816 child_thread
= GetWindowThreadProcessId (cp
.hwnd
, NULL
);
817 if (child_thread
== GetCurrentThreadId ()
818 || !AttachThreadInput (GetCurrentThreadId (),
822 /* Set the foreground window to the child. */
823 if (SetForegroundWindow (cp
.hwnd
))
825 /* Generate keystrokes as if user had typed Ctrl-Break or
827 keybd_event (VK_CONTROL
, control_scan_code
, 0, 0);
828 keybd_event (vk_break_code
, break_scan_code
,
829 (vk_break_code
== 'C' ? 0 : KEYEVENTF_EXTENDEDKEY
), 0);
830 keybd_event (vk_break_code
, break_scan_code
,
831 (vk_break_code
== 'C' ? 0 : KEYEVENTF_EXTENDEDKEY
)
832 | KEYEVENTF_KEYUP
, 0);
833 keybd_event (VK_CONTROL
, control_scan_code
, KEYEVENTF_KEYUP
, 0);
835 /* Sleep for a bit to give time for the main frame to respond
836 to focus change events. */
839 SetForegroundWindow (foreground_window
);
841 /* Detach from the foreground and child threads now that
842 the foreground switching is over. */
843 if (foreground_thread
)
844 AttachThreadInput (GetCurrentThreadId (), foreground_thread
, FALSE
);
846 AttachThreadInput (GetCurrentThreadId (), child_thread
, FALSE
);
849 /* Ctrl-Break is NT equivalent of SIGINT. */
850 else if (!GenerateConsoleCtrlEvent
851 (CTRL_BREAK_EVENT
, cp
.procinfo
->dwProcessId
))
861 /* kill a process, as this implementation use CreateProcess on Win32 we need
862 to use Win32 TerminateProcess API */
864 __gnat_terminate_process (struct TTY_Process
* p
)
870 if (p
->usePipe
== TRUE
) {
873 buf
[0] = EXP_SLAVE_KILL
;
874 buf
[1] = EXP_KILL_TERMINATE
;
875 bret
= WriteFile (p
->w_infd
, buf
, 2, &written
, NULL
);
879 if (!TerminateProcess (p
->procinfo
.hProcess
, 1))
887 /* wait for process pid to terminate and return the process status. This
888 implementation is different from the adaint.c one for Windows as it uses
889 the Win32 API instead of the C one. */
892 __gnat_tty_waitpid (struct TTY_Process
* p
)
896 HANDLE proc_hand
= p
->procinfo
.hProcess
;
898 res
= WaitForSingleObject (proc_hand
, 0);
899 GetExitCodeProcess (proc_hand
, &exitcode
);
901 CloseHandle (p
->procinfo
.hThread
);
902 CloseHandle (p
->procinfo
.hProcess
);
904 /* No need to close the handles: they were closed on the ada side */
906 return (int) exitcode
;
909 /********************************
910 ** __gnat_free_process ()
911 ********************************/
914 __gnat_free_process (struct TTY_Process
** process
)
923 int tty_fd
; /* descriptor for the tty */
924 char tty_name
[24]; /* Name of TTY device */
928 __gnat_tty_supported (void)
933 /* Return the tty name associated with p */
936 __gnat_tty_name (TTY_Handle
* t
)
942 __gnat_tty_fd (TTY_Handle
* t
)
948 __gnat_new_tty (void)
950 return (TTY_Handle
*)0;
954 __gnat_reset_tty (TTY_Handle
* t
)
960 __gnat_close_tty (TTY_Handle
* t
)
966 __gnat_setup_winsize (void *desc
, int rows
, int columns
)
970 #else /* defined(_WIN32, implementatin for all UNIXes */
972 /* First defined some macro to identify easily some systems */
973 #if defined (__FreeBSD__) \
974 || defined (__OpenBSD__) \
975 || defined (__NetBSD__) \
976 || defined (__DragonFly__)
979 #if defined (__mips) && defined (__sgi)
983 /* Include every system header we need */
989 /* On some system termio is either absent or including it will disable termios
991 #if ! defined (__hpux__) && ! defined (FREEBSD) && \
992 ! defined (__APPLE__) && ! defined(__rtems__)
996 #include <sys/ioctl.h>
1000 #include <sys/stat.h>
1001 #include <sys/types.h>
1002 #include <sys/wait.h>
1005 # include <sys/stropts.h>
1007 #if defined (FREEBSD) || defined (sun)
1008 # include <sys/signal.h>
1010 #if defined (__hpux__)
1011 # include <sys/termio.h>
1012 # include <sys/stropts.h>
1015 #define CDISABLE _POSIX_VDISABLE
1017 /* On HP-UX and Sun system, there is a bzero function but with a different
1018 signature. Use memset instead */
1019 #if defined (__hpux__) || defined (sun) || defined (_AIX)
1020 # define bzero(s,n) memset (s,0,n)
1023 /* POSIX does not specify how to open the master side of a terminal.Several
1024 methods are available (system specific):
1025 1- using a cloning device (USE_CLONE_DEVICE)
1026 2- getpt (USE_GETPT)
1027 3- openpty (USE_OPENPTY)
1028 4- _getpty (USE_GETPTY)
1030 When using the cloning device method, the macro USE_CLONE_DEVICE should
1031 contains a full path to the adequate device.
1033 When a new system is about to be supported, one of the previous macro should
1034 be set otherwise allocate_pty_desc will return an error
1037 /* Configurable part */
1038 #if defined (__APPLE__) || defined (FREEBSD)
1040 #elif defined (IRIX)
1042 #elif defined (linux)
1045 #define USE_CLONE_DEVICE "/dev/ptmx"
1046 #elif defined (_AIX)
1047 #define USE_CLONE_DEVICE "/dev/ptc"
1048 #elif defined (__hpux__)
1049 /* On HP-UX we use the streamed version. Using the non streamed version is not
1050 recommanded (through "/dev/ptym/clone"). Indeed it seems that there are
1051 issues to detect process terminations. */
1052 #define USE_CLONE_DEVICE "/dev/ptmx"
1055 /* structure that holds information about the terminal used and the process
1056 connected on the slave side */
1057 typedef struct pty_desc_struct
{
1058 int master_fd
; /* fd of the master side if the terminal */
1059 int slave_fd
; /* fd of the slave side */
1060 char slave_name
[32]; /* filename of the slave side */
1061 int child_pid
; /* PID of the child process connected to the slave side
1065 /* allocate_pty_desc - allocate a pseudo terminal
1068 * out desc returned pointer to a pty_desc structure containing information
1069 * about the opened pseudo terminal
1074 * If the function is successful we should have at least the master side fd
1075 * and the slave side filename. On some system, the slave side will also be
1076 * opened. If this is not the case the slave side will be open once we are in
1077 * the child process (note that opening the slave side at this stage will
1081 extern char* ptsname (int);
1084 allocate_pty_desc (pty_desc
**desc
) {
1090 char *slave_name
= NULL
;
1093 master_fd
= getpt ();
1094 #elif defined (USE_OPENPTY)
1095 status
= openpty (&master_fd
, &slave_fd
, NULL
, NULL
, NULL
);
1096 #elif defined (USE_GETPTY)
1097 slave_name
= _getpty (&master_fd
, O_RDWR
| O_NDELAY
, 0600, 0);
1098 if (slave_name
== NULL
) status
= -1;
1099 #elif defined (USE_CLONE_DEVICE)
1100 master_fd
= open (USE_CLONE_DEVICE
, O_RDWR
| O_NONBLOCK
, 0);
1102 printf ("[error]: terminal support is not configured\n");
1106 /* at this stage we should have the master side fd and status should be 0 */
1107 if (status
!= 0 || master_fd
< 0)
1109 /* If this is not the case close all opened files and return -1 */
1110 printf ("[error]: cannot allocate master side of the pty\n");
1111 if (master_fd
>= 0) close (master_fd
);
1112 if (slave_fd
>= 0) close (slave_fd
);
1117 /* retrieve the file name of the slave side if necessary */
1118 if (slave_name
== NULL
) slave_name
= (char *) ptsname (master_fd
);
1120 /* Now we should have slave file name */
1121 if (slave_name
== NULL
)
1123 /* If not the case close any opened file and return - 1 */
1124 printf ("[error]: cannot allocate slave side of the pty\n");
1125 if (master_fd
>= 0) close (master_fd
);
1126 if (slave_fd
>= 0) close (slave_fd
);
1131 #if !defined(__rtems__)
1132 /* grant access to the slave side */
1133 grantpt (master_fd
);
1134 /* unlock the terminal */
1135 unlockpt (master_fd
);
1138 /* set desc and return 0 */
1139 result
= malloc (sizeof (pty_desc
));
1140 result
->master_fd
= master_fd
;
1141 result
->slave_fd
= slave_fd
;
1142 /* the string returned by ptsname or _getpty is a static allocated string. So
1143 we should make a copy */
1144 strncpy (result
->slave_name
, slave_name
, sizeof (result
->slave_name
));
1145 result
->slave_name
[sizeof (result
->slave_name
) - 1] = '\0';
1146 result
->child_pid
= -1;
1151 /* some utility macro that make the code of child_setup_tty easier to read */
1152 #define __enable(a, b) ((a) |= (b))
1153 #define __disable(a, b) ((a) &= ~(b))
1155 /* some properties do not exist on all systems. Set their value to 0 in that
1172 /* child_setup_tty - set terminal properties
1175 * file descriptor of the slave side of the terminal
1178 * 0 if success, any other value if failed.
1184 child_setup_tty (int fd
)
1189 /* ensure that s is filled with 0 */
1190 bzero (&s
, sizeof (&s
));
1192 /* Get the current terminal settings */
1193 status
= tcgetattr (fd
, &s
);
1194 if (status
!= 0) return -1;
1196 /* Adjust input modes */
1197 __disable (s
.c_iflag
, IUCLC
); /* don't transform to lower case */
1198 __disable (s
.c_iflag
, ISTRIP
); /* don't delete 8th bit */
1200 /* Adjust output modes */
1201 __enable (s
.c_oflag
, OPOST
); /* enable postprocessing */
1202 __disable (s
.c_oflag
, ONLCR
); /* don't map LF to CR-LF */
1203 __disable (s
.c_oflag
, NLDLY
|CRDLY
|TABDLY
|BSDLY
|VTDLY
|FFDLY
);
1204 /* disable delays */
1205 __disable (s
.c_oflag
, OLCUC
); /* don't transform to upper case */
1207 /* Adjust control modes */
1208 s
.c_cflag
= (s
.c_cflag
& ~CSIZE
) | CS8
; /* Don't strip 8th bit */
1210 /* Adjust local modes */
1211 __disable (s
.c_lflag
, ECHO
); /* disable echo */
1212 __enable (s
.c_lflag
, ISIG
); /* enable signals */
1213 __enable (s
.c_lflag
, ICANON
); /* erase/kill/eof processing */
1215 /* Adjust control characters */
1216 /* IMPORTANT: we need to ensure that Ctrl-C will trigger an interrupt signal
1217 otherwise send_signal_via_characters will fail */
1218 s
.c_cc
[VEOF
] = 04; /* insure that EOF is Control-D */
1219 s
.c_cc
[VERASE
] = CDISABLE
; /* disable erase processing */
1220 s
.c_cc
[VKILL
] = CDISABLE
; /* disable kill processing */
1221 s
.c_cc
[VQUIT
] = 28; /* Control-\ */
1222 s
.c_cc
[VINTR
] = 03; /* Control-C */
1223 s
.c_cc
[VEOL
] = CDISABLE
;
1224 s
.c_cc
[VSUSP
] = 26; /* Control-Z */
1226 /* push our changes */
1227 status
= tcsetattr (fd
, TCSADRAIN
, &s
);
1231 /* __gnat_setup_communication - interface to the external world. Should be
1232 * called before forking. On Unixes this function only call allocate_pty_desc.
1233 * The Windows implementation (in different part of this file) is very
1237 * out desc returned pointer to a pty_desc structure
1239 * 0 if success, -1 otherwise
1241 int __gnat_setup_communication (pty_desc
** desc
) {
1242 return allocate_pty_desc (desc
);
1245 /* __gnat_setup_parent_communication - interface to the external world. Should
1246 * be called after forking in the parent process
1251 out err_fd fds corresponding to the parent side of the
1253 in pid_out child process pid
1258 __gnat_setup_parent_communication
1260 int* in_fd
, /* input */
1261 int* out_fd
, /* output */
1262 int* err_fd
, /* error */
1266 *in_fd
= desc
->master_fd
;
1267 *out_fd
= desc
->master_fd
;
1268 *err_fd
= desc
->master_fd
;
1269 desc
->child_pid
= *pid_out
;
1272 /* __gnat_setup_winsize - Sets up the size of the terminal
1273 * This lets the process know the size of the terminal
1276 void __gnat_setup_winsize (pty_desc
*desc
, int rows
, int columns
) {
1279 s
.ws_row
= (unsigned short)rows
;
1280 s
.ws_col
= (unsigned short)columns
;
1283 ioctl (desc
->master_fd
, TIOCSWINSZ
, &s
);
1285 if (desc
->child_pid
> 0) {
1286 /* Let the process know about the change in size */
1287 kill (desc
->child_pid
, SIGWINCH
);
1293 /* __gnat_setup_child_communication - interface to external world. Should be
1294 * called after forking in the child process. On Unixes, this function
1295 * first adjust the line setting, set standard output, input and error and
1296 * then spawn the program.
1299 * desc a pty_desc structure containing the pty parameters
1300 * new_argv argv of the program to be spawned
1302 * this function should not return
1305 __gnat_setup_child_communication
1311 int pid
= getpid ();
1315 /* open the slave side of the terminal if necessary */
1316 if (desc
->slave_fd
== -1)
1318 /* On AIX, if the slave process is not opened with O_NDELAY or O_NONBLOCK
1319 then we might have some processes hanging on I/O system calls. Not sure
1320 we can do that for all platforms so do it only on AIX for the moment.
1321 On AIX O_NONBLOCK and O_NDELAY have slightly different meanings. When
1322 reading on the slave fd, in case there is no data available, if O_NDELAY
1323 is set then 0 is returned. If O_NON_BLOCK is -1 is returned. It seems
1324 that interactive programs such as GDB prefer the O_NDELAY behavior.
1325 We chose O_NONBLOCK because it allows us to make the distinction
1326 between a true EOF and an EOF returned because there is no data
1327 available to be read. */
1328 desc
->slave_fd
= open (desc
->slave_name
, O_RDWR
| O_NONBLOCK
, 0);
1330 desc
->slave_fd
= open (desc
->slave_name
, O_RDWR
, 0);
1333 #if defined (sun) || defined (__hpux__)
1334 /* On systems such as Solaris we are using stream. We need to push the right
1335 "modules" in order to get the expected terminal behaviors. Otherwise
1336 functionalities such as termios are not available. */
1337 ioctl (desc
->slave_fd
, I_PUSH
, "ptem");
1338 ioctl (desc
->slave_fd
, I_PUSH
, "ldterm");
1339 ioctl (desc
->slave_fd
, I_PUSH
, "ttcompat");
1343 /* make the tty the controling terminal */
1344 status
= ioctl (desc
->slave_fd
, TIOCSCTTY
, 0);
1347 /* adjust tty settings */
1348 child_setup_tty (desc
->slave_fd
);
1349 __gnat_setup_winsize (desc
, 24, 80); /* To prevent errors in some shells */
1351 /* stdin, stdout and stderr should be now our tty */
1352 dup2 (desc
->slave_fd
, 0);
1353 dup2 (desc
->slave_fd
, 1);
1354 dup2 (desc
->slave_fd
, 2);
1355 if (desc
->slave_fd
> 2) close (desc
->slave_fd
);
1357 /* adjust process group settings */
1358 status
= setpgid (pid
, pid
);
1359 status
= tcsetpgrp (0, pid
);
1361 /* launch the program */
1362 execvp (new_argv
[0], new_argv
);
1364 /* return the pid */
1368 /* send_signal_via_characters - Send a characters that will trigger a signal
1369 * in the child process.
1372 * desc a pty_desc structure containing terminal information
1373 * int a signal number
1378 send_signal_via_characters
1383 char ctrl_backslash
= 28;
1386 switch (signal_number
)
1389 write (desc
->master_fd
, &ctrl_c
, 1); return;
1391 write (desc
->master_fd
, &ctrl_backslash
, 1); return;
1393 write (desc
->master_fd
, &ctrl_Z
, 1); return;
1397 /* __gnat_interrupt_process - interrupt the child process
1400 * desc a pty_desc structure
1403 __gnat_interrupt_process (pty_desc
*desc
)
1405 send_signal_via_characters (desc
, SIGINT
);
1409 /* __gnat_interrupt_pid - interrupt a process group
1412 * pid pid of the process to interrupt
1415 __gnat_interrupt_pid (int pid
)
1417 kill (-pid
, SIGINT
);
1421 /* __gnat_terminate_process - kill a child process
1424 * desc pty_desc structure
1426 int __gnat_terminate_process (pty_desc
*desc
)
1428 return kill (desc
->child_pid
, SIGKILL
);
1431 /* __gnat_tty_waitpid - wait for the child proces to die
1434 * desc pty_desc structure
1436 * exit status of the child process
1439 __gnat_tty_waitpid (pty_desc
*desc
)
1442 waitpid (desc
->child_pid
, &status
, 0);
1443 return WEXITSTATUS (status
);
1446 /* __gnat_tty_supported - Are tty supported ?
1449 * always 1 on Unix systems
1452 __gnat_tty_supported (void)
1457 /* __gnat_free_process - free a pty_desc structure
1460 * in out desc: a pty desc structure
1463 __gnat_free_process (pty_desc
** desc
)
1469 /* __gnat_send_header - dummy function. this interface is only used on Windows */
1471 __gnat_send_header (pty_desc
* desc
, char header
[5], int size
, int *ret
)
1476 /* __gnat_reset_tty - reset line setting
1479 * desc: a pty_desc structure
1482 __gnat_reset_tty (pty_desc
* desc
)
1484 child_setup_tty (desc
->master_fd
);
1487 /* __gnat_new_tty - allocate a new terminal
1490 * a pty_desc structure
1493 __gnat_new_tty (void)
1497 status
= allocate_pty_desc (&desc
);
1498 child_setup_tty (desc
->master_fd
);
1502 /* __gnat_close_tty - close a terminal
1505 * desc a pty_desc strucure
1507 void __gnat_close_tty (pty_desc
* desc
)
1509 if (desc
->master_fd
>= 0) close (desc
->master_fd
);
1510 if (desc
->slave_fd
>= 0) close (desc
->slave_fd
);
1513 /* __gnat_tty_name - return slave side device name
1516 * desc a pty_desc strucure
1521 __gnat_tty_name (pty_desc
* desc
)
1523 return desc
->slave_name
;
1526 /* __gnat_tty_name - return master side fd
1529 * desc a pty_desc strucure
1534 __gnat_tty_fd (pty_desc
* desc
)
1536 return desc
->master_fd
;