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__) || \
37 void * __gnat_new_tty (void) { return (void*)0; }
38 char * __gnat_tty_name (void* t) { return (char*)0; }
39 int __gnat_interrupt_pid (int pid) { return -1; }
40 int __gnat_interrupt_process (void* desc) { return -1; }
41 int __gnat_setup_communication (void** desc) { return -1; }
42 void __gnat_setup_parent_communication
43 (void* d, int* i, int* o, int*e, int*p) { return -1; }
44 int __gnat_setup_child_communication
45 (void* d, char **n, int u) { return -1; }
46 int __gnat_terminate_process (void *desc) { return -1; }
47 int __gnat_tty_fd (void* t) { return -1; }
48 int __gnat_tty_supported (void) { return 0; }
49 int __gnat_tty_waitpid (void *desc) { return 1; }
50 void __gnat_close_tty (void* t) {}
51 void __gnat_free_process (void** process) {}
52 void __gnat_reset_tty (void* t) {}
53 void __gnat_send_header (void* d, char h[5], int s, int *r) {}
54 void __gnat_setup_winsize (void *desc, int rows, int columns) {}
56 /* For Windows platforms. */
66 #define MAXPATHLEN 1024
68 #define NILP(x) ((x) == 0)
70 #define report_file_error(x, y) fprintf (stderr, "Error: %s\n", x);
75 int pid; /* Number of this process */
76 PROCESS_INFORMATION procinfo;
77 HANDLE w_infd, w_outfd;
78 HANDLE w_forkin, w_forkout;
82 /* Control whether create_child cause the process to inherit GPS'
83 error mode setting. The default is 1, to minimize the possibility of
84 subprocesses blocking when accessing unmounted drives. */
85 static int Vw32_start_process_inherit_error_mode = 1;
87 /* Control whether spawnve quotes arguments as necessary to ensure
88 correct parsing by child process. Because not all uses of spawnve
89 are careful about constructing argv arrays, we make this behaviour
90 conditional (off by default, since a similar operation is already done
91 in g-expect.adb by calling Normalize_Argument). */
92 static int Vw32_quote_process_args = 0;
94 static DWORD AbsoluteSeek(HANDLE, DWORD);
95 static VOID ReadBytes(HANDLE, LPVOID, DWORD);
97 #define XFER_BUFFER_SIZE 2048
99 /* This tell if the executable we're about to launch uses a GUI interface. */
100 /* if we can't determine it, we will return true */
102 is_gui_app (char *exe)
109 DWORD CoffHeaderOffset;
110 DWORD MoreDosHeader[16];
116 IMAGE_DOS_HEADER image_dos_header;
117 IMAGE_FILE_HEADER image_file_header;
118 IMAGE_OPTIONAL_HEADER image_optional_header;
119 IMAGE_SECTION_HEADER image_section_header;
122 * Open the reference file.
130 file = malloc ((nlen + 1) * sizeof (char));
131 memcpy (file, &exe[1], nlen);
135 hImage = CreateFile(file,
140 FILE_ATTRIBUTE_NORMAL,
147 if (INVALID_HANDLE_VALUE == hImage)
149 report_file_error ("Could not open exe: ", Qnil);
150 report_file_error (exe, Qnil);
151 report_file_error ("\n", Qnil);
152 CloseHandle (hImage);
157 * Read the MS-DOS image header.
159 ReadBytes(hImage, &image_dos_header, sizeof(IMAGE_DOS_HEADER));
161 if (IMAGE_DOS_SIGNATURE != image_dos_header.e_magic)
163 report_file_error("Sorry, I do not understand this file.\n", Qnil);
164 CloseHandle (hImage);
169 * Read more MS-DOS header. */
170 ReadBytes(hImage, MoreDosHeader, sizeof(MoreDosHeader));
172 * Get actual COFF header.
174 CoffHeaderOffset = AbsoluteSeek(hImage, image_dos_header.e_lfanew) +
176 if (CoffHeaderOffset < 0) {
177 CloseHandle (hImage);
181 ReadBytes (hImage, &ntSignature, sizeof(ULONG));
183 if (IMAGE_NT_SIGNATURE != ntSignature)
185 report_file_error ("Missing NT signature. Unknown file type.\n", Qnil);
186 CloseHandle (hImage);
190 SectionOffset = CoffHeaderOffset + IMAGE_SIZEOF_FILE_HEADER +
191 IMAGE_SIZEOF_NT_OPTIONAL_HEADER;
193 ReadBytes(hImage, &image_file_header, IMAGE_SIZEOF_FILE_HEADER);
196 * Read optional header.
199 &image_optional_header,
200 IMAGE_SIZEOF_NT_OPTIONAL_HEADER);
202 CloseHandle (hImage);
204 switch (image_optional_header.Subsystem)
206 case IMAGE_SUBSYSTEM_UNKNOWN:
210 case IMAGE_SUBSYSTEM_NATIVE:
214 case IMAGE_SUBSYSTEM_WINDOWS_GUI:
218 case IMAGE_SUBSYSTEM_WINDOWS_CUI:
222 case IMAGE_SUBSYSTEM_OS2_CUI:
226 case IMAGE_SUBSYSTEM_POSIX_CUI:
231 /* Unknown, return GUI app to be preservative: if yes, it will be
232 correctly launched, if no, it will be launched, and a console will
233 be also displayed, which is not a big deal */
241 AbsoluteSeek (HANDLE hFile, DWORD offset)
245 newOffset = SetFilePointer (hFile, offset, NULL, FILE_BEGIN);
247 if (newOffset == 0xFFFFFFFF)
254 ReadBytes (HANDLE hFile, LPVOID buffer, DWORD size)
258 if (!ReadFile(hFile, buffer, size, &bytes, NULL))
263 else if (size != bytes)
270 nt_spawnve (char *exe, char **argv, char *env, struct TTY_Process *process)
273 SECURITY_ATTRIBUTES sec_attrs;
274 SECURITY_DESCRIPTOR sec_desc;
276 char dir[ MAXPATHLEN ];
279 char *cmdline, *parg, **targ;
284 /* we have to do some conjuring here to put argv and envp into the
285 form CreateProcess wants... argv needs to be a space separated/null
286 terminated list of parameters, and envp is a null
287 separated/double-null terminated list of parameters.
289 Additionally, zero-length args and args containing whitespace or
290 quote chars need to be wrapped in double quotes - for this to work,
291 embedded quotes need to be escaped as well. The aim is to ensure
292 the child process reconstructs the argv array we start with
293 exactly, so we treat quotes at the beginning and end of arguments
296 Note that using backslash to escape embedded quotes requires
297 additional special handling if an embedded quote is already
298 preceeded by backslash, or if an arg requiring quoting ends with
299 backslash. In such cases, the run of escape characters needs to be
300 doubled. For consistency, we apply this special handling as long
301 as the escape character is not quote.
303 Since we have no idea how large argv and envp are likely to be we
304 figure out list lengths on the fly and allocate them. */
306 if (!NILP (Vw32_quote_process_args))
309 /* Override escape char by binding w32-quote-process-args to
310 desired character, or use t for auto-selection. */
311 if (INTEGERP (Vw32_quote_process_args))
312 escape_char = XINT (Vw32_quote_process_args);
324 int escape_char_run = 0;
332 /* allow for embedded quotes to be escaped */
335 /* handle the case where the embedded quote is already escaped */
336 if (escape_char_run > 0)
338 /* To preserve the arg exactly, we need to double the
339 preceding escape characters (plus adding one to
340 escape the quote character itself). */
341 arglen += escape_char_run;
344 else if (*p == ' ' || *p == '\t')
349 if (*p == escape_char && escape_char != '"')
357 /* handle the case where the arg ends with an escape char - we
358 must not let the enclosing quote be escaped. */
359 if (escape_char_run > 0)
360 arglen += escape_char_run;
362 arglen += strlen (*targ) + 1;
366 is_gui = is_gui_app (argv[0]);
370 /* could not determine application type. Try launching with "cmd /c" */
376 cmdline = (char*)malloc (arglen + 1);
380 if (use_cmd == TRUE) {
381 strcpy (parg, "cmd /c ");
396 if (*p == ' ' || *p == '\t' || *p == '"')
401 int escape_char_run = 0;
407 last = p + strlen (p) - 1;
413 /* double preceding escape chars if any */
414 while (escape_char_run > 0)
416 *parg++ = escape_char;
419 /* escape all quote chars, even at beginning or end */
420 *parg++ = escape_char;
424 if (*p == escape_char && escape_char != '"')
429 /* double escape chars before enclosing quote */
430 while (escape_char_run > 0)
432 *parg++ = escape_char;
439 strcpy (parg, *targ);
440 parg += strlen (*targ);
447 memset (&start, 0, sizeof (start));
448 start.cb = sizeof (start);
450 if (process->usePipe == TRUE) {
451 start.dwFlags = STARTF_USESTDHANDLES;
452 start.hStdInput = process->w_forkin;
453 start.hStdOutput = process->w_forkout;
454 /* child's stderr is always redirected to outfd */
455 start.hStdError = process->w_forkout;
457 start.dwFlags = STARTF_USESTDHANDLES;
458 /* We only need to redirect stderr/stdout here. Stdin will be forced to
459 the spawned process console by explaunch */
460 start.hStdInput = NULL;
461 start.hStdOutput = process->w_forkout;
462 start.hStdError = process->w_forkout;
465 /* Explicitly specify no security */
466 if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
468 if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
470 sec_attrs.nLength = sizeof (sec_attrs);
471 sec_attrs.lpSecurityDescriptor = &sec_desc;
472 sec_attrs.bInheritHandle = FALSE;
474 /* creating a new console allow easier close. Do not use
475 CREATE_NEW_PROCESS_GROUP as this results in disabling Ctrl+C */
476 flags = CREATE_NEW_CONSOLE;
477 if (NILP (Vw32_start_process_inherit_error_mode))
478 flags |= CREATE_DEFAULT_ERROR_MODE;
480 /* if app is not a gui application, hide the console */
481 if (is_gui == FALSE) {
482 start.dwFlags |= STARTF_USESHOWWINDOW;
483 start.wShowWindow = SW_HIDE;
486 /* Set initial directory to null character to use current directory */
487 if (!CreateProcess (NULL, cmdline, &sec_attrs, NULL, TRUE,
488 flags, env, NULL, &start, &process->procinfo))
491 pid = (int) process->procinfo.hProcess;
500 /*************************
501 ** __gnat_send_header ()
502 *************************/
504 #define EXP_SLAVE_CREATE 'c'
505 #define EXP_SLAVE_KEY 'k'
506 #define EXP_SLAVE_MOUSE 'm'
507 #define EXP_SLAVE_WRITE 'w'
508 #define EXP_SLAVE_KILL 'x'
510 #define EXP_KILL_TERMINATE 0x1
511 #define EXP_KILL_CTRL_C 0x2
512 #define EXP_KILL_CTRL_BREAK 0x4
515 __gnat_send_header (struct TTY_Process* p, char header[5], int size, int *ret)
517 if (p->usePipe == FALSE) {
518 header[0] = EXP_SLAVE_WRITE;
519 header[1] = size & 0xff;
520 header[2] = (size & 0xff00) >> 8;
521 header[3] = (size & 0xff0000) >> 16;
522 header[4] = (size & 0xff000000) >> 24;
529 /**********************************
530 ** __gnat_setup_communication ()
531 **********************************/
534 __gnat_setup_communication (struct TTY_Process** process_out) /* output param */
536 struct TTY_Process* process;
538 process = (struct TTY_Process*)malloc (sizeof (struct TTY_Process));
539 ZeroMemory (process, sizeof (struct TTY_Process));
540 *process_out = process;
545 #define EXP_PIPE_BASENAME "\\\\.\\pipe\\ExpectPipe"
548 __gnat_setup_child_communication
549 (struct TTY_Process* process,
555 SECURITY_ATTRIBUTES sec_attrs;
556 char slavePath [MAX_PATH];
560 char pipeNameIn[100];
561 HANDLE hSlaveInDrv = NULL; /* Handle to communicate with slave driver */
563 parent = GetCurrentProcess ();
565 /* Set inheritance for the pipe handles */
566 sec_attrs.nLength = sizeof (SECURITY_ATTRIBUTES);
567 sec_attrs.bInheritHandle = TRUE;
568 sec_attrs.lpSecurityDescriptor = NULL;
571 /* Create in and out pipes */
572 if (!CreatePipe (&process->w_forkin, &process->w_infd, &sec_attrs, 0))
573 report_file_error ("Creation of child's IN handle", Qnil);
574 if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
575 report_file_error ("Creation of child's OUT handle", Qnil);
577 /* Do not inherit the parent's side of the pipes */
578 SetHandleInformation (&process->w_infd, HANDLE_FLAG_INHERIT, 0);
579 SetHandleInformation (&process->w_outfd, HANDLE_FLAG_INHERIT, 0);
581 /* use native argv */
583 process->usePipe = TRUE;
586 static int pipeNameId = 0;
588 process->w_infd = NULL;
590 /* We create a named pipe for Input, as we handle input by sending special
591 commands to the explaunch process, that uses it to feed the actual input
593 sprintf(pipeNameIn, "%sIn%08x_%08x", EXP_PIPE_BASENAME,
594 GetCurrentProcessId(), pipeNameId);
597 hSlaveInDrv = CreateNamedPipe(pipeNameIn,
598 PIPE_ACCESS_OUTBOUND,
599 PIPE_TYPE_BYTE | PIPE_WAIT, 1, 8192, 8192,
601 if (hSlaveInDrv == NULL) goto end;
603 if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
604 report_file_error ("Creation of child's OUT handle", Qnil);
606 if (SearchPath (NULL, "explaunch.exe", NULL,
607 MAX_PATH, slavePath, NULL) == 0) goto end;
609 for (argc=0; argv[argc] != NULL; argc++) ;
610 nargv = (char **) malloc (sizeof (char*) * (argc + 3));
611 nargv[0] = slavePath;
612 nargv[1] = pipeNameIn;
614 for (i = 0; i <= argc; i++) nargv[i + 2] = argv[i];
615 process->usePipe = FALSE;
618 /* Spawn the child. */
619 cpid = nt_spawnve (nargv[0], nargv, NULL, process);
621 /* close the duplicated handles passed to the child */
622 CloseHandle (process->w_forkout);
624 if (process->usePipe == TRUE) {
625 CloseHandle (process->w_forkin);
628 UCHAR buf[8]; /* enough space for child status info */
634 * Wait for connection with the slave driver
636 bRet = ConnectNamedPipe(hSlaveInDrv, NULL);
638 dwRet = GetLastError();
639 if (dwRet == ERROR_PIPE_CONNECTED) {
646 process->w_infd = hSlaveInDrv;
649 * wait for slave driver to initialize before allowing user to send to it
651 bRet = ReadFile(process->w_outfd, buf, 8, &count, NULL);
656 dwRet = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
661 cpid = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24);
666 /* An error occurred while trying to spawn the process. */
667 report_file_error ("Spawning child process", Qnil);
671 if (hSlaveInDrv != NULL)
672 CloseHandle (hSlaveInDrv);
677 __gnat_setup_parent_communication
678 (struct TTY_Process* process,
684 *in = _open_osfhandle ((long) process->w_infd, 0);
685 *out = _open_osfhandle ((long) process->w_outfd, 0);
686 /* child's stderr is always redirected to outfd */
691 typedef struct _child_process
694 PROCESS_INFORMATION *procinfo;
697 /* The major and minor versions of NT. */
698 static int w32_major_version;
699 static int w32_minor_version;
701 /* Distinguish between Windows NT and Windows 95. */
702 static enum {OS_UNKNOWN, OS_WIN95, OS_NT} os_subtype = OS_UNKNOWN;
704 /* Cache information describing the NT system for later use. */
706 cache_system_info (void)
719 /* Cache the version of the operating system. */
720 version.data = GetVersion ();
721 w32_major_version = version.info.major;
722 w32_minor_version = version.info.minor;
724 if (version.info.platform & 0x8000)
725 os_subtype = OS_WIN95;
731 find_child_console (HWND hwnd, child_process * cp)
736 thread_id = GetWindowThreadProcessId (hwnd, &process_id);
737 if (process_id == cp->procinfo->dwProcessId)
739 char window_class[32];
741 GetClassName (hwnd, window_class, sizeof (window_class));
742 if (strcmp (window_class,
743 (os_subtype == OS_WIN95)
745 : "ConsoleWindowClass") == 0)
756 __gnat_interrupt_process (struct TTY_Process* p)
762 if (p->usePipe == TRUE) {
765 buf[0] = EXP_SLAVE_KILL;
766 buf[1] = EXP_KILL_CTRL_C;
767 bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
771 return __gnat_interrupt_pid (p->procinfo.dwProcessId);
777 __gnat_interrupt_pid (int pid)
779 volatile child_process cp;
782 cp.procinfo = (LPPROCESS_INFORMATION) malloc (sizeof (PROCESS_INFORMATION));
783 cp.procinfo->dwProcessId = pid;
785 if (os_subtype == OS_UNKNOWN)
786 cache_system_info ();
788 /* Try to locate console window for process. */
789 EnumWindows ((WNDENUMPROC) find_child_console, (LPARAM) &cp);
793 BYTE control_scan_code = (BYTE) MapVirtualKey (VK_CONTROL, 0);
794 /* Retrieve Ctrl-C scancode */
795 BYTE vk_break_code = 'C';
796 BYTE break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0);
797 HWND foreground_window;
799 foreground_window = GetForegroundWindow ();
800 if (foreground_window)
802 /* NT 5.0, and apparently also Windows 98, will not allow
803 a Window to be set to foreground directly without the
804 user's involvement. The workaround is to attach
805 ourselves to the thread that owns the foreground
806 window, since that is the only thread that can set the
807 foreground window. */
808 DWORD foreground_thread, child_thread;
811 GetWindowThreadProcessId (foreground_window, NULL);
812 if (foreground_thread == GetCurrentThreadId ()
813 || !AttachThreadInput (GetCurrentThreadId (),
814 foreground_thread, TRUE))
815 foreground_thread = 0;
817 child_thread = GetWindowThreadProcessId (cp.hwnd, NULL);
818 if (child_thread == GetCurrentThreadId ()
819 || !AttachThreadInput (GetCurrentThreadId (),
823 /* Set the foreground window to the child. */
824 if (SetForegroundWindow (cp.hwnd))
826 /* Generate keystrokes as if user had typed Ctrl-Break or
828 keybd_event (VK_CONTROL, control_scan_code, 0, 0);
829 keybd_event (vk_break_code, break_scan_code,
830 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY), 0);
831 keybd_event (vk_break_code, break_scan_code,
832 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY)
833 | KEYEVENTF_KEYUP, 0);
834 keybd_event (VK_CONTROL, control_scan_code, KEYEVENTF_KEYUP, 0);
836 /* Sleep for a bit to give time for the main frame to respond
837 to focus change events. */
840 SetForegroundWindow (foreground_window);
842 /* Detach from the foreground and child threads now that
843 the foreground switching is over. */
844 if (foreground_thread)
845 AttachThreadInput (GetCurrentThreadId (), foreground_thread, FALSE);
847 AttachThreadInput (GetCurrentThreadId (), child_thread, FALSE);
850 /* Ctrl-Break is NT equivalent of SIGINT. */
851 else if (!GenerateConsoleCtrlEvent
852 (CTRL_BREAK_EVENT, cp.procinfo->dwProcessId))
862 /* kill a process, as this implementation use CreateProcess on Win32 we need
863 to use Win32 TerminateProcess API */
865 __gnat_terminate_process (struct TTY_Process* p)
871 if (p->usePipe == TRUE) {
874 buf[0] = EXP_SLAVE_KILL;
875 buf[1] = EXP_KILL_TERMINATE;
876 bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
880 if (!TerminateProcess (p->procinfo.hProcess, 1))
888 /* wait for process pid to terminate and return the process status. This
889 implementation is different from the adaint.c one for Windows as it uses
890 the Win32 API instead of the C one. */
893 __gnat_tty_waitpid (struct TTY_Process* p)
897 HANDLE proc_hand = p->procinfo.hProcess;
899 res = WaitForSingleObject (proc_hand, 0);
900 GetExitCodeProcess (proc_hand, &exitcode);
902 CloseHandle (p->procinfo.hThread);
903 CloseHandle (p->procinfo.hProcess);
905 /* No need to close the handles: they were closed on the ada side */
907 return (int) exitcode;
910 /********************************
911 ** __gnat_free_process ()
912 ********************************/
915 __gnat_free_process (struct TTY_Process** process)
924 int tty_fd; /* descriptor for the tty */
925 char tty_name[24]; /* Name of TTY device */
929 __gnat_tty_supported (void)
934 /* Return the tty name associated with p */
937 __gnat_tty_name (TTY_Handle* t)
943 __gnat_tty_fd (TTY_Handle* t)
949 __gnat_new_tty (void)
951 return (TTY_Handle*)0;
955 __gnat_reset_tty (TTY_Handle* t)
961 __gnat_close_tty (TTY_Handle* t)
967 __gnat_setup_winsize (void *desc, int rows, int columns)
971 #else /* defined(_WIN32, implementatin for all UNIXes */
973 /* First defined some macro to identify easily some systems */
974 #if defined (__FreeBSD__) \
975 || defined (__OpenBSD__) \
976 || defined (__NetBSD__) \
977 || defined (__DragonFly__)
981 /* Include every system header we need */
987 /* On some system termio is either absent or including it will disable termios
989 #if ! defined (__hpux__) && ! defined (FREEBSD) && \
990 ! defined (__APPLE__) && ! defined(__rtems__)
994 #include <sys/ioctl.h>
998 #include <sys/stat.h>
999 #include <sys/types.h>
1000 #include <sys/wait.h>
1003 # include <sys/stropts.h>
1005 #if defined (FREEBSD) || defined (sun)
1006 # include <sys/signal.h>
1008 #if defined (__hpux__)
1009 # include <sys/termio.h>
1010 # include <sys/stropts.h>
1013 #define CDISABLE _POSIX_VDISABLE
1015 /* On HP-UX and Sun system, there is a bzero function but with a different
1016 signature. Use memset instead */
1017 #if defined (__hpux__) || defined (sun) || defined (_AIX)
1018 # define bzero(s,n) memset (s,0,n)
1021 /* POSIX does not specify how to open the master side of a terminal.Several
1022 methods are available (system specific):
1023 1- using a cloning device (USE_CLONE_DEVICE)
1024 2- getpt (USE_GETPT)
1025 3- openpty (USE_OPENPTY)
1027 When using the cloning device method, the macro USE_CLONE_DEVICE should
1028 contains a full path to the adequate device.
1030 When a new system is about to be supported, one of the previous macro should
1031 be set otherwise allocate_pty_desc will return an error
1034 /* Configurable part */
1035 #if defined (__APPLE__) || defined (FREEBSD)
1037 #elif defined (linux)
1040 #define USE_CLONE_DEVICE "/dev/ptmx"
1041 #elif defined (_AIX)
1042 #define USE_CLONE_DEVICE "/dev/ptc"
1043 #elif defined (__hpux__)
1044 /* On HP-UX we use the streamed version. Using the non streamed version is not
1045 recommanded (through "/dev/ptym/clone"). Indeed it seems that there are
1046 issues to detect process terminations. */
1047 #define USE_CLONE_DEVICE "/dev/ptmx"
1050 /* structure that holds information about the terminal used and the process
1051 connected on the slave side */
1052 typedef struct pty_desc_struct {
1053 int master_fd; /* fd of the master side if the terminal */
1054 int slave_fd; /* fd of the slave side */
1055 char slave_name[32]; /* filename of the slave side */
1056 int child_pid; /* PID of the child process connected to the slave side
1060 /* allocate_pty_desc - allocate a pseudo terminal
1063 * out desc returned pointer to a pty_desc structure containing information
1064 * about the opened pseudo terminal
1069 * If the function is successful we should have at least the master side fd
1070 * and the slave side filename. On some system, the slave side will also be
1071 * opened. If this is not the case the slave side will be open once we are in
1072 * the child process (note that opening the slave side at this stage will
1076 extern char* ptsname (int);
1079 allocate_pty_desc (pty_desc **desc) {
1085 char *slave_name = NULL;
1088 master_fd = getpt ();
1089 #elif defined (USE_OPENPTY)
1090 status = openpty (&master_fd, &slave_fd, NULL, NULL, NULL);
1091 #elif defined (USE_CLONE_DEVICE)
1092 master_fd = open (USE_CLONE_DEVICE, O_RDWR | O_NONBLOCK, 0);
1094 printf ("[error]: terminal support is not configured\n");
1098 /* at this stage we should have the master side fd and status should be 0 */
1099 if (status != 0 || master_fd < 0)
1101 /* If this is not the case close all opened files and return -1 */
1102 printf ("[error]: cannot allocate master side of the pty\n");
1103 if (master_fd >= 0) close (master_fd);
1104 if (slave_fd >= 0) close (slave_fd);
1109 /* retrieve the file name of the slave side if necessary */
1110 if (slave_name == NULL) slave_name = (char *) ptsname (master_fd);
1112 /* Now we should have slave file name */
1113 if (slave_name == NULL)
1115 /* If not the case close any opened file and return - 1 */
1116 printf ("[error]: cannot allocate slave side of the pty\n");
1117 if (master_fd >= 0) close (master_fd);
1118 if (slave_fd >= 0) close (slave_fd);
1123 #if !defined(__rtems__)
1124 /* grant access to the slave side */
1125 grantpt (master_fd);
1126 /* unlock the terminal */
1127 unlockpt (master_fd);
1130 /* set desc and return 0 */
1131 result = malloc (sizeof (pty_desc));
1132 result->master_fd = master_fd;
1133 result->slave_fd = slave_fd;
1134 /* the string returned by ptsname or _getpty is a static allocated string. So
1135 we should make a copy */
1136 strncpy (result->slave_name, slave_name, sizeof (result->slave_name));
1137 result->slave_name[sizeof (result->slave_name) - 1] = '\0';
1138 result->child_pid = -1;
1143 /* some utility macro that make the code of child_setup_tty easier to read */
1144 #define __enable(a, b) ((a) |= (b))
1145 #define __disable(a, b) ((a) &= ~(b))
1147 /* some properties do not exist on all systems. Set their value to 0 in that
1164 /* child_setup_tty - set terminal properties
1167 * file descriptor of the slave side of the terminal
1170 * 0 if success, any other value if failed.
1176 child_setup_tty (int fd)
1181 /* ensure that s is filled with 0 */
1182 bzero (&s, sizeof (&s));
1184 /* Get the current terminal settings */
1185 status = tcgetattr (fd, &s);
1186 if (status != 0) return -1;
1188 /* Adjust input modes */
1189 __disable (s.c_iflag, IUCLC); /* don't transform to lower case */
1190 __disable (s.c_iflag, ISTRIP); /* don't delete 8th bit */
1192 /* Adjust output modes */
1193 __enable (s.c_oflag, OPOST); /* enable postprocessing */
1194 __disable (s.c_oflag, ONLCR); /* don't map LF to CR-LF */
1195 __disable (s.c_oflag, NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
1196 /* disable delays */
1197 __disable (s.c_oflag, OLCUC); /* don't transform to upper case */
1199 /* Adjust control modes */
1200 s.c_cflag = (s.c_cflag & ~CSIZE) | CS8; /* Don't strip 8th bit */
1202 /* Adjust local modes */
1203 __disable (s.c_lflag, ECHO); /* disable echo */
1204 __enable (s.c_lflag, ISIG); /* enable signals */
1205 __enable (s.c_lflag, ICANON); /* erase/kill/eof processing */
1207 /* Adjust control characters */
1208 /* IMPORTANT: we need to ensure that Ctrl-C will trigger an interrupt signal
1209 otherwise send_signal_via_characters will fail */
1210 s.c_cc[VEOF] = 04; /* insure that EOF is Control-D */
1211 s.c_cc[VERASE] = CDISABLE; /* disable erase processing */
1212 s.c_cc[VKILL] = CDISABLE; /* disable kill processing */
1213 s.c_cc[VQUIT] = 28; /* Control-\ */
1214 s.c_cc[VINTR] = 03; /* Control-C */
1215 s.c_cc[VEOL] = CDISABLE;
1216 s.c_cc[VSUSP] = 26; /* Control-Z */
1218 /* push our changes */
1219 status = tcsetattr (fd, TCSADRAIN, &s);
1223 /* __gnat_setup_communication - interface to the external world. Should be
1224 * called before forking. On Unixes this function only call allocate_pty_desc.
1225 * The Windows implementation (in different part of this file) is very
1229 * out desc returned pointer to a pty_desc structure
1231 * 0 if success, -1 otherwise
1233 int __gnat_setup_communication (pty_desc** desc) {
1234 return allocate_pty_desc (desc);
1237 /* __gnat_setup_parent_communication - interface to the external world. Should
1238 * be called after forking in the parent process
1243 out err_fd fds corresponding to the parent side of the
1245 in pid_out child process pid
1250 __gnat_setup_parent_communication
1252 int* in_fd, /* input */
1253 int* out_fd, /* output */
1254 int* err_fd, /* error */
1258 *in_fd = desc->master_fd;
1259 *out_fd= desc->master_fd;
1260 *err_fd= desc->master_fd;
1261 desc->child_pid = *pid_out;
1264 /* __gnat_setup_winsize - Sets up the size of the terminal
1265 * This lets the process know the size of the terminal
1268 void __gnat_setup_winsize (pty_desc *desc, int rows, int columns) {
1271 s.ws_row = (unsigned short)rows;
1272 s.ws_col = (unsigned short)columns;
1275 ioctl (desc->master_fd, TIOCSWINSZ, &s);
1277 if (desc->child_pid > 0) {
1278 /* Let the process know about the change in size */
1279 kill (desc->child_pid, SIGWINCH);
1285 /* __gnat_setup_child_communication - interface to external world. Should be
1286 * called after forking in the child process. On Unixes, this function
1287 * first adjust the line setting, set standard output, input and error and
1288 * then spawn the program.
1291 * desc a pty_desc structure containing the pty parameters
1292 * new_argv argv of the program to be spawned
1294 * this function should not return
1297 __gnat_setup_child_communication
1303 int pid = getpid ();
1307 /* open the slave side of the terminal if necessary */
1308 if (desc->slave_fd == -1)
1310 /* On AIX, if the slave process is not opened with O_NDELAY or O_NONBLOCK
1311 then we might have some processes hanging on I/O system calls. Not sure
1312 we can do that for all platforms so do it only on AIX for the moment.
1313 On AIX O_NONBLOCK and O_NDELAY have slightly different meanings. When
1314 reading on the slave fd, in case there is no data available, if O_NDELAY
1315 is set then 0 is returned. If O_NON_BLOCK is -1 is returned. It seems
1316 that interactive programs such as GDB prefer the O_NDELAY behavior.
1317 We chose O_NONBLOCK because it allows us to make the distinction
1318 between a true EOF and an EOF returned because there is no data
1319 available to be read. */
1320 desc->slave_fd = open (desc->slave_name, O_RDWR | O_NONBLOCK, 0);
1322 desc->slave_fd = open (desc->slave_name, O_RDWR, 0);
1325 #if defined (sun) || defined (__hpux__)
1326 /* On systems such as Solaris we are using stream. We need to push the right
1327 "modules" in order to get the expected terminal behaviors. Otherwise
1328 functionalities such as termios are not available. */
1329 ioctl (desc->slave_fd, I_PUSH, "ptem");
1330 ioctl (desc->slave_fd, I_PUSH, "ldterm");
1331 ioctl (desc->slave_fd, I_PUSH, "ttcompat");
1335 /* make the tty the controling terminal */
1336 status = ioctl (desc->slave_fd, TIOCSCTTY, 0);
1339 /* adjust tty settings */
1340 child_setup_tty (desc->slave_fd);
1341 __gnat_setup_winsize (desc, 24, 80); /* To prevent errors in some shells */
1343 /* stdin, stdout and stderr should be now our tty */
1344 dup2 (desc->slave_fd, 0);
1345 dup2 (desc->slave_fd, 1);
1346 dup2 (desc->slave_fd, 2);
1347 if (desc->slave_fd > 2) close (desc->slave_fd);
1349 /* adjust process group settings */
1350 status = setpgid (pid, pid);
1351 status = tcsetpgrp (0, pid);
1353 /* launch the program */
1354 execvp (new_argv[0], new_argv);
1356 /* return the pid */
1360 /* send_signal_via_characters - Send a characters that will trigger a signal
1361 * in the child process.
1364 * desc a pty_desc structure containing terminal information
1365 * int a signal number
1370 send_signal_via_characters
1375 char ctrl_backslash = 28;
1378 switch (signal_number)
1381 write (desc->master_fd, &ctrl_c, 1); return;
1383 write (desc->master_fd, &ctrl_backslash, 1); return;
1385 write (desc->master_fd, &ctrl_Z, 1); return;
1389 /* __gnat_interrupt_process - interrupt the child process
1392 * desc a pty_desc structure
1395 __gnat_interrupt_process (pty_desc *desc)
1397 send_signal_via_characters (desc, SIGINT);
1401 /* __gnat_interrupt_pid - interrupt a process group
1404 * pid pid of the process to interrupt
1407 __gnat_interrupt_pid (int pid)
1409 kill (-pid, SIGINT);
1413 /* __gnat_terminate_process - kill a child process
1416 * desc pty_desc structure
1418 int __gnat_terminate_process (pty_desc *desc)
1420 return kill (desc->child_pid, SIGKILL);
1423 /* __gnat_tty_waitpid - wait for the child proces to die
1426 * desc pty_desc structure
1428 * exit status of the child process
1431 __gnat_tty_waitpid (pty_desc *desc)
1434 waitpid (desc->child_pid, &status, 0);
1435 return WEXITSTATUS (status);
1438 /* __gnat_tty_supported - Are tty supported ?
1441 * always 1 on Unix systems
1444 __gnat_tty_supported (void)
1449 /* __gnat_free_process - free a pty_desc structure
1452 * in out desc: a pty desc structure
1455 __gnat_free_process (pty_desc** desc)
1461 /* __gnat_send_header - dummy function. this interface is only used on Windows */
1463 __gnat_send_header (pty_desc* desc, char header[5], int size, int *ret)
1468 /* __gnat_reset_tty - reset line setting
1471 * desc: a pty_desc structure
1474 __gnat_reset_tty (pty_desc* desc)
1476 child_setup_tty (desc->master_fd);
1479 /* __gnat_new_tty - allocate a new terminal
1482 * a pty_desc structure
1485 __gnat_new_tty (void)
1489 status = allocate_pty_desc (&desc);
1490 child_setup_tty (desc->master_fd);
1494 /* __gnat_close_tty - close a terminal
1497 * desc a pty_desc strucure
1499 void __gnat_close_tty (pty_desc* desc)
1501 if (desc->master_fd >= 0) close (desc->master_fd);
1502 if (desc->slave_fd >= 0) close (desc->slave_fd);
1505 /* __gnat_tty_name - return slave side device name
1508 * desc a pty_desc strucure
1513 __gnat_tty_name (pty_desc* desc)
1515 return desc->slave_name;
1518 /* __gnat_tty_name - return master side fd
1521 * desc a pty_desc strucure
1526 __gnat_tty_fd (pty_desc* desc)
1528 return desc->master_fd;