1139825Simp/**************************************************************************** 286227Stmm * * 3128776Stmm * GNAT RUN-TIME COMPONENTS * 4167308Smarius * * 5128776Stmm * T E R M I N A L S * 686227Stmm * * 786227Stmm * C Implementation File * 886227Stmm * * 986227Stmm * Copyright (C) 2008-2015, AdaCore * 1086227Stmm * * 1186227Stmm * GNAT is free software; you can redistribute it and/or modify it under * 1286227Stmm * terms of the GNU General Public License as published by the Free Soft- * 1386227Stmm * ware Foundation; either version 3, or (at your option) any later ver- * 1486227Stmm * sion. GNAT is distributed in the hope that it will be useful, but WITH- * 1586227Stmm * OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * 1686227Stmm * or FITNESS FOR A PARTICULAR PURPOSE. * 1786227Stmm * * 18128776Stmm * As a special exception under Section 7 of GPL version 3, you are granted * 1986227Stmm * additional permissions described in the GCC Runtime Library Exception, * 2086227Stmm * version 3.1, as published by the Free Software Foundation. * 2186227Stmm * * 2286227Stmm * You should have received a copy of the GNU General Public License and * 2386227Stmm * a copy of the GCC Runtime Library Exception along with this program; * 2486227Stmm * see the files COPYING3 and COPYING.RUNTIME respectively. If not, see * 2586227Stmm * <http://www.gnu.org/licenses/>. * 2686227Stmm * * 2786227Stmm * GNAT was originally developed by the GNAT team at New York University. * 2886227Stmm * Extensive contributions were provided by Ada Core Technologies Inc. * 2986227Stmm * * 3086227Stmm ****************************************************************************/ 3186227Stmm 3286227Stmm/* First all usupported platforms. Add stubs for exported routines. */ 3386227Stmm 3486227Stmm#if defined (VMS) || defined (__vxworks) || defined (__Lynx__) \ 35146474Smarius || defined (__ANDROID__) || defined (__PikeOS__) 36146474Smarius 37146474Smarius#define ATTRIBUTE_UNUSED __attribute__((unused)) 3886227Stmm 3986227Stmmvoid * 4086227Stmm__gnat_new_tty (void) 4186227Stmm{ 4286227Stmm return (void*)0; 43130068Sphk} 4486227Stmm 45167308Smariuschar * 46167308Smarius__gnat_tty_name (void* t ATTRIBUTE_UNUSED) 4786227Stmm{ 4886227Stmm return (char*)0; 4986227Stmm} 50167308Smarius 5186227Stmmint 52167308Smarius__gnat_interrupt_pid (int pid ATTRIBUTE_UNUSED) 5386227Stmm{ 54167308Smarius return -1; 5586227Stmm} 5686227Stmm 5786227Stmmint 5886227Stmm__gnat_interrupt_process (void* desc ATTRIBUTE_UNUSED) 5986227Stmm{ 60133862Smarius return -1; 6186227Stmm} 62128776Stmm 6386227Stmmint 6486227Stmm__gnat_setup_communication (void** desc ATTRIBUTE_UNUSED) 6586227Stmm{ 6686227Stmm return -1; 6786227Stmm} 68133862Smarius 6986227Stmmvoid 7086227Stmm__gnat_setup_parent_communication (void *d ATTRIBUTE_UNUSED, 7186227Stmm int *i ATTRIBUTE_UNUSED, 72167308Smarius int *o ATTRIBUTE_UNUSED, 73167308Smarius int *e ATTRIBUTE_UNUSED, 7486227Stmm int *p ATTRIBUTE_UNUSED) 7586227Stmm{ 7688823Stmm} 7788823Stmm 7888823Stmmint 7988823Stmm__gnat_setup_child_communication (void *d ATTRIBUTE_UNUSED, 8088823Stmm char **n ATTRIBUTE_UNUSED, 81128776Stmm int u ATTRIBUTE_UNUSED) 82128776Stmm{ 83167308Smarius return -1; 84146474Smarius} 85128776Stmm 86128776Stmmint 87128776Stmm__gnat_terminate_process (void *desc ATTRIBUTE_UNUSED) 88128776Stmm{ 89128776Stmm return -1; 90128776Stmm} 91128776Stmm 92167308Smariusint 93167308Smarius__gnat_tty_fd (void* t ATTRIBUTE_UNUSED) 94167308Smarius{ 9586227Stmm return -1; 96178443Smarius} 97178443Smarius 98178443Smariusint 99185133Smarius__gnat_tty_supported (void) 100167308Smarius{ 101167308Smarius return 0; 102167308Smarius} 103167308Smarius 10486227Stmmint 10586227Stmm__gnat_tty_waitpid (void *desc ATTRIBUTE_UNUSED) 10686227Stmm{ 107128776Stmm return 1; 10886227Stmm} 10986227Stmm 11086227Stmmvoid 11186227Stmm__gnat_close_tty (void* t ATTRIBUTE_UNUSED) 11286227Stmm{ 113167308Smarius} 114167308Smarius 115167308Smariusvoid 116167308Smarius__gnat_free_process (void** process ATTRIBUTE_UNUSED) 117167308Smarius{ 118146474Smarius} 11986227Stmm 12086227Stmmvoid 12186227Stmm__gnat_reset_tty (void* t ATTRIBUTE_UNUSED) 12286227Stmm{ 123167308Smarius} 124167308Smarius 125190099Smariusvoid 126190099Smarius__gnat_send_header (void* d ATTRIBUTE_UNUSED, 127190099Smarius char h[5] ATTRIBUTE_UNUSED, 128178443Smarius int s ATTRIBUTE_UNUSED, 129178443Smarius int *r ATTRIBUTE_UNUSED) 130178443Smarius{ 131167308Smarius} 13286227Stmm 133167308Smariusvoid 134167308Smarius__gnat_setup_winsize (void *desc ATTRIBUTE_UNUSED, 135167308Smarius int rows ATTRIBUTE_UNUSED, 136167308Smarius int columns ATTRIBUTE_UNUSED) 137167308Smarius{ 138167308Smarius} 139167308Smarius 140167308Smarius/* For Windows platforms. */ 141190099Smarius 14286227Stmm#elif defined(_WIN32) 14386227Stmm 14486227Stmm#include <errno.h> 14586227Stmm#include <stdio.h> 146167308Smarius#include <stdlib.h> 14786227Stmm 148200815Smarius#include <windows.h> 14986227Stmm 150185133Smarius#define MAXPATHLEN 1024 15190622Stmm 152167308Smarius#define NILP(x) ((x) == 0) 15390622Stmm#define Qnil 0 15490622Stmm#define report_file_error(x, y) fprintf (stderr, "Error: %s\n", x); 15586227Stmm#define INTEGERP(x) 1 15690622Stmm#define XINT(x) x 15786227Stmm 15886227Stmmstruct TTY_Process { 159167308Smarius int pid; /* Number of this process */ 16090622Stmm PROCESS_INFORMATION procinfo; 16186227Stmm HANDLE w_infd, w_outfd; 16286227Stmm HANDLE w_forkin, w_forkout; 16386227Stmm BOOL usePipe; 164185133Smarius}; 16586227Stmm 16686227Stmm/* Control whether create_child cause the process to inherit GPS' 16786227Stmm error mode setting. The default is 1, to minimize the possibility of 16886227Stmm subprocesses blocking when accessing unmounted drives. */ 16986227Stmmstatic int Vw32_start_process_inherit_error_mode = 1; 17086227Stmm 17186227Stmm/* Control whether spawnve quotes arguments as necessary to ensure 17286227Stmm correct parsing by child process. Because not all uses of spawnve 173185133Smarius are careful about constructing argv arrays, we make this behaviour 17486227Stmm conditional (off by default, since a similar operation is already done 17586227Stmm in g-expect.adb by calling Normalize_Argument). */ 17686227Stmmstatic int Vw32_quote_process_args = 0; 177167308Smarius 178167308Smariusstatic DWORD AbsoluteSeek(HANDLE, DWORD); 17986227Stmmstatic VOID ReadBytes(HANDLE, LPVOID, DWORD); 18086227Stmm 18186227Stmm#define XFER_BUFFER_SIZE 2048 18286227Stmm 18386227Stmm/* This tell if the executable we're about to launch uses a GUI interface. */ 18486227Stmm/* if we can't determine it, we will return true */ 18586227Stmmstatic int 18686227Stmmis_gui_app (char *exe) 18786227Stmm{ 18886227Stmm HANDLE hImage; 18986227Stmm 19086227Stmm DWORD bytes; 19186227Stmm DWORD iSection; 192128776Stmm DWORD SectionOffset; 193128776Stmm DWORD CoffHeaderOffset; 194133862Smarius DWORD MoreDosHeader[16]; 195128776Stmm CHAR *file; 196128776Stmm size_t nlen; 197128776Stmm 198128776Stmm ULONG ntSignature; 199128776Stmm 200128776Stmm IMAGE_DOS_HEADER image_dos_header; 201167308Smarius IMAGE_FILE_HEADER image_file_header; 202167308Smarius IMAGE_OPTIONAL_HEADER image_optional_header; 20386227Stmm IMAGE_SECTION_HEADER image_section_header; 204167308Smarius 20586227Stmm /* 206167308Smarius * Open the reference file. 207167308Smarius */ 208167308Smarius nlen = strlen (exe); 20986227Stmm file = exe; 21088823Stmm if (nlen > 2) { 21188823Stmm if (exe[0] == '"') { 21288823Stmm /* remove quotes */ 21388823Stmm nlen -= 2; 214167308Smarius file = malloc ((nlen + 1) * sizeof (char)); 21588823Stmm memcpy (file, &exe[1], nlen); 21688823Stmm file [nlen] = '\0'; 21797265Sjake } 218167308Smarius } 219167308Smarius hImage = CreateFile(file, 220146474Smarius GENERIC_READ, 221146474Smarius FILE_SHARE_READ, 222146474Smarius NULL, 223146474Smarius OPEN_EXISTING, 224146474Smarius FILE_ATTRIBUTE_NORMAL, 225146474Smarius NULL); 226146474Smarius 227146474Smarius if (file != exe) { 228146474Smarius free (file); 229167308Smarius } 230167308Smarius 23186227Stmm if (INVALID_HANDLE_VALUE == hImage) 232167308Smarius { 233167308Smarius report_file_error ("Could not open exe: ", Qnil); 234167308Smarius report_file_error (exe, Qnil); 235167308Smarius report_file_error ("\n", Qnil); 236167308Smarius CloseHandle (hImage); 23786227Stmm return -1; 23888823Stmm } 239167308Smarius 24086227Stmm /* 241128776Stmm * Read the MS-DOS image header. 24286227Stmm */ 24386227Stmm ReadBytes(hImage, &image_dos_header, sizeof(IMAGE_DOS_HEADER)); 244146474Smarius 245146474Smarius if (IMAGE_DOS_SIGNATURE != image_dos_header.e_magic) 246146474Smarius { 247146474Smarius report_file_error("Sorry, I do not understand this file.\n", Qnil); 248167308Smarius CloseHandle (hImage); 249146474Smarius return -1; 250146474Smarius } 251146474Smarius 252146474Smarius /* 253146474Smarius * Read more MS-DOS header. */ 254167308Smarius ReadBytes(hImage, MoreDosHeader, sizeof(MoreDosHeader)); 255167308Smarius /* 256167308Smarius * Get actual COFF header. 257167308Smarius */ 258167308Smarius CoffHeaderOffset = AbsoluteSeek(hImage, image_dos_header.e_lfanew) + 259146474Smarius sizeof(ULONG); 260146474Smarius if (CoffHeaderOffset < 0) { 261146474Smarius CloseHandle (hImage); 262146474Smarius return -1; 263167308Smarius } 264167308Smarius 26586227Stmm ReadBytes (hImage, &ntSignature, sizeof(ULONG)); 266167308Smarius 26786227Stmm if (IMAGE_NT_SIGNATURE != ntSignature) 268167308Smarius { 269167308Smarius report_file_error ("Missing NT signature. Unknown file type.\n", Qnil); 270167308Smarius CloseHandle (hImage); 271167308Smarius return -1; 27286227Stmm } 27386227Stmm 274167308Smarius SectionOffset = CoffHeaderOffset + IMAGE_SIZEOF_FILE_HEADER + 275167308Smarius IMAGE_SIZEOF_NT_OPTIONAL_HEADER; 27686227Stmm 277167308Smarius ReadBytes(hImage, &image_file_header, IMAGE_SIZEOF_FILE_HEADER); 27886227Stmm 279167308Smarius /* 280167308Smarius * Read optional header. 281167308Smarius */ 282167308Smarius ReadBytes(hImage, 283167308Smarius &image_optional_header, 28486227Stmm IMAGE_SIZEOF_NT_OPTIONAL_HEADER); 28586227Stmm 28686227Stmm CloseHandle (hImage); 28786227Stmm 288166901Spiso switch (image_optional_header.Subsystem) 28986227Stmm { 29086227Stmm case IMAGE_SUBSYSTEM_UNKNOWN: 29188823Stmm return 1; 29286227Stmm break; 293167308Smarius 29486227Stmm case IMAGE_SUBSYSTEM_NATIVE: 295131535Simp return 1; 29686227Stmm break; 29786227Stmm 298128776Stmm case IMAGE_SUBSYSTEM_WINDOWS_GUI: 29986227Stmm return 1; 30086227Stmm break; 30186227Stmm 30286227Stmm case IMAGE_SUBSYSTEM_WINDOWS_CUI: 303131535Simp return 0; 304166901Spiso break; 30586227Stmm 306167308Smarius case IMAGE_SUBSYSTEM_OS2_CUI: 307167308Smarius return 0; 308167308Smarius break; 309167308Smarius 310167308Smarius case IMAGE_SUBSYSTEM_POSIX_CUI: 31186227Stmm return 0; 31286227Stmm break; 31386227Stmm 31486227Stmm default: 31586227Stmm /* Unknown, return GUI app to be preservative: if yes, it will be 31686227Stmm correctly launched, if no, it will be launched, and a console will 317146474Smarius be also displayed, which is not a big deal */ 318131535Simp return 1; 31986227Stmm break; 32086227Stmm } 32186227Stmm 322178443Smarius} 323178443Smarius 324178443Smariusstatic DWORD 325178443SmariusAbsoluteSeek (HANDLE hFile, DWORD offset) 326178443Smarius{ 327178443Smarius DWORD newOffset; 328178443Smarius 329178443Smarius newOffset = SetFilePointer (hFile, offset, NULL, FILE_BEGIN); 330178443Smarius 33186227Stmm if (newOffset == 0xFFFFFFFF) 33286227Stmm return -1; 33386227Stmm else 33486227Stmm return newOffset; 335167308Smarius} 336167308Smarius 337167308Smariusstatic VOID 338167308SmariusReadBytes (HANDLE hFile, LPVOID buffer, DWORD size) 339167308Smarius{ 34086227Stmm DWORD bytes; 341167308Smarius 342167308Smarius if (!ReadFile(hFile, buffer, size, &bytes, NULL)) 343167308Smarius { 344167308Smarius size = 0; 345167308Smarius return; 34686227Stmm } 347167308Smarius else if (size != bytes) 348167308Smarius { 349167308Smarius return; 350167308Smarius } 351167308Smarius} 352167308Smarius 353167308Smariusstatic int 354167308Smariusnt_spawnve (char *exe, char **argv, char *env, struct TTY_Process *process) 355167308Smarius{ 356167308Smarius STARTUPINFO start; 357167308Smarius SECURITY_ATTRIBUTES sec_attrs; 358167308Smarius SECURITY_DESCRIPTOR sec_desc; 359167308Smarius DWORD flags; 360167308Smarius char dir[ MAXPATHLEN ]; 36186227Stmm int pid; 36286227Stmm int is_gui, use_cmd; 36388823Stmm char *cmdline, *parg, **targ; 36486227Stmm int do_quoting = 0; 36588823Stmm char escape_char; 36688823Stmm int arglen; 36788823Stmm 36886227Stmm /* we have to do some conjuring here to put argv and envp into the 36986227Stmm form CreateProcess wants... argv needs to be a space separated/null 37086227Stmm terminated list of parameters, and envp is a null 37186227Stmm separated/double-null terminated list of parameters. 372167308Smarius 37386227Stmm Additionally, zero-length args and args containing whitespace or 37486227Stmm quote chars need to be wrapped in double quotes - for this to work, 37586227Stmm embedded quotes need to be escaped as well. The aim is to ensure 376157896Simp the child process reconstructs the argv array we start with 37788823Stmm exactly, so we treat quotes at the beginning and end of arguments 37888823Stmm as embedded quotes. 37988823Stmm 38088823Stmm Note that using backslash to escape embedded quotes requires 38186227Stmm additional special handling if an embedded quote is already 38286227Stmm preceded by backslash, or if an arg requiring quoting ends with 383128776Stmm backslash. In such cases, the run of escape characters needs to be 38486227Stmm doubled. For consistency, we apply this special handling as long 38586227Stmm as the escape character is not quote. 38686227Stmm 38786227Stmm Since we have no idea how large argv and envp are likely to be we 38888823Stmm figure out list lengths on the fly and allocate them. */ 389167308Smarius 390167308Smarius if (!NILP (Vw32_quote_process_args)) 391167308Smarius { 392167308Smarius do_quoting = 1; 393167308Smarius /* Override escape char by binding w32-quote-process-args to 394167308Smarius desired character, or use t for auto-selection. */ 395167308Smarius if (INTEGERP (Vw32_quote_process_args)) 39686227Stmm escape_char = XINT (Vw32_quote_process_args); 39786227Stmm else 39886227Stmm escape_char = '\\'; 39986227Stmm } 40086227Stmm 40186227Stmm /* do argv... */ 40286227Stmm arglen = 0; 40386227Stmm targ = argv; 40486227Stmm while (*targ) 40586227Stmm { 40686227Stmm char *p = *targ; 40786227Stmm int need_quotes = 0; 40886227Stmm int escape_char_run = 0; 40986227Stmm 41086227Stmm if (*p == 0) 41186227Stmm need_quotes = 1; 41288823Stmm for ( ; *p; p++) 41386227Stmm { 41486227Stmm if (*p == '"') 41586227Stmm { 41686227Stmm /* allow for embedded quotes to be escaped */ 41786227Stmm arglen++; 41886227Stmm need_quotes = 1; 419167308Smarius /* handle the case where the embedded quote is already escaped */ 42086227Stmm if (escape_char_run > 0) 42186227Stmm { 42286227Stmm /* To preserve the arg exactly, we need to double the 42386227Stmm preceding escape characters (plus adding one to 42486227Stmm escape the quote character itself). */ 42586227Stmm arglen += escape_char_run; 426128776Stmm } 42786227Stmm } 42886227Stmm else if (*p == ' ' || *p == '\t') 42986227Stmm { 430167308Smarius need_quotes = 1; 431167308Smarius } 432167308Smarius 433167308Smarius if (*p == escape_char && escape_char != '"') 434167308Smarius escape_char_run++; 435167308Smarius else 436167308Smarius escape_char_run = 0; 437167308Smarius } 438167308Smarius if (need_quotes) 439167308Smarius { 440167308Smarius arglen += 2; 441167308Smarius /* handle the case where the arg ends with an escape char - we 442167308Smarius must not let the enclosing quote be escaped. */ 443167308Smarius if (escape_char_run > 0) 444167308Smarius arglen += escape_char_run; 445167308Smarius } 446167308Smarius arglen += strlen (*targ) + 1; 447167308Smarius targ++; 448167308Smarius } 449167308Smarius 450167308Smarius is_gui = is_gui_app (argv[0]); 451167308Smarius use_cmd = FALSE; 452167308Smarius 453167308Smarius if (is_gui == -1) { 454167308Smarius /* could not determine application type. Try launching with "cmd /c" */ 455167308Smarius is_gui = FALSE; 456167308Smarius arglen += 7; 457167308Smarius use_cmd = TRUE; 458167308Smarius } 459167308Smarius 460167308Smarius cmdline = (char*)malloc (arglen + 1); 461167308Smarius targ = argv; 462167308Smarius parg = cmdline; 463167308Smarius 464167308Smarius if (use_cmd == TRUE) { 465167308Smarius strcpy (parg, "cmd /c "); 466167308Smarius parg += 7; 467167308Smarius } 468167308Smarius 469167308Smarius while (*targ) 470167308Smarius { 471167308Smarius char * p = *targ; 472167308Smarius int need_quotes = 0; 473167308Smarius 474167308Smarius if (*p == 0) 475167308Smarius need_quotes = 1; 476167308Smarius 477167308Smarius if (do_quoting) 478167308Smarius { 479167308Smarius for ( ; *p; p++) 480167308Smarius if (*p == ' ' || *p == '\t' || *p == '"') 481167308Smarius need_quotes = 1; 482167308Smarius } 483167308Smarius if (need_quotes) 484167308Smarius { 485167308Smarius int escape_char_run = 0; 486167308Smarius char * first; 487167308Smarius char * last; 488167308Smarius 489167308Smarius p = *targ; 490167308Smarius first = p; 491167308Smarius last = p + strlen (p) - 1; 492167308Smarius *parg++ = '"'; 493167308Smarius for ( ; *p; p++) 494167308Smarius { 495167308Smarius if (*p == '"') 496167308Smarius { 497167308Smarius /* double preceding escape chars if any */ 498167308Smarius while (escape_char_run > 0) 499167308Smarius { 500167308Smarius *parg++ = escape_char; 501167308Smarius escape_char_run--; 502167308Smarius } 503167308Smarius /* escape all quote chars, even at beginning or end */ 504167308Smarius *parg++ = escape_char; 505167308Smarius } 506167308Smarius *parg++ = *p; 507167308Smarius 508167308Smarius if (*p == escape_char && escape_char != '"') 509167308Smarius escape_char_run++; 510167308Smarius else 511167308Smarius escape_char_run = 0; 512167308Smarius } 513167308Smarius /* double escape chars before enclosing quote */ 514167308Smarius while (escape_char_run > 0) 515167308Smarius { 516167308Smarius *parg++ = escape_char; 517167308Smarius escape_char_run--; 518167308Smarius } 519167308Smarius *parg++ = '"'; 520167308Smarius } 521167308Smarius else 522167308Smarius { 523167308Smarius strcpy (parg, *targ); 524167308Smarius parg += strlen (*targ); 525167308Smarius } 526167308Smarius *parg++ = ' '; 527167308Smarius targ++; 528167308Smarius } 529167308Smarius *--parg = '\0'; 530167308Smarius 531167308Smarius memset (&start, 0, sizeof (start)); 532167308Smarius start.cb = sizeof (start); 533167308Smarius 534167308Smarius if (process->usePipe == TRUE) { 535167308Smarius start.dwFlags = STARTF_USESTDHANDLES; 536167308Smarius start.hStdInput = process->w_forkin; 537167308Smarius start.hStdOutput = process->w_forkout; 538167308Smarius /* child's stderr is always redirected to outfd */ 539167308Smarius start.hStdError = process->w_forkout; 540167308Smarius } else { 541167308Smarius start.dwFlags = STARTF_USESTDHANDLES; 542167308Smarius /* We only need to redirect stderr/stdout here. Stdin will be forced to 543167308Smarius the spawned process console by explaunch */ 544 start.hStdInput = NULL; 545 start.hStdOutput = process->w_forkout; 546 start.hStdError = process->w_forkout; 547 } 548 549 /* Explicitly specify no security */ 550 if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION)) 551 goto EH_Fail; 552 if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE)) 553 goto EH_Fail; 554 sec_attrs.nLength = sizeof (sec_attrs); 555 sec_attrs.lpSecurityDescriptor = &sec_desc; 556 sec_attrs.bInheritHandle = FALSE; 557 558 /* creating a new console allow easier close. Do not use 559 CREATE_NEW_PROCESS_GROUP as this results in disabling Ctrl+C */ 560 flags = CREATE_NEW_CONSOLE; 561 if (NILP (Vw32_start_process_inherit_error_mode)) 562 flags |= CREATE_DEFAULT_ERROR_MODE; 563 564 /* if app is not a gui application, hide the console */ 565 if (is_gui == FALSE) { 566 start.dwFlags |= STARTF_USESHOWWINDOW; 567 start.wShowWindow = SW_HIDE; 568 } 569 570 /* Set initial directory to null character to use current directory */ 571 if (!CreateProcess (NULL, cmdline, &sec_attrs, NULL, TRUE, 572 flags, env, NULL, &start, &process->procinfo)) 573 goto EH_Fail; 574 575 pid = (int) process->procinfo.hProcess; 576 process->pid=pid; 577 578 return pid; 579 580 EH_Fail: 581 return -1; 582} 583 584/************************* 585 ** __gnat_send_header () 586 *************************/ 587 588#define EXP_SLAVE_CREATE 'c' 589#define EXP_SLAVE_KEY 'k' 590#define EXP_SLAVE_MOUSE 'm' 591#define EXP_SLAVE_WRITE 'w' 592#define EXP_SLAVE_KILL 'x' 593 594#define EXP_KILL_TERMINATE 0x1 595#define EXP_KILL_CTRL_C 0x2 596#define EXP_KILL_CTRL_BREAK 0x4 597 598void 599__gnat_send_header (struct TTY_Process* p, char header[5], int size, int *ret) 600{ 601 if (p->usePipe == FALSE) { 602 header[0] = EXP_SLAVE_WRITE; 603 header[1] = size & 0xff; 604 header[2] = (size & 0xff00) >> 8; 605 header[3] = (size & 0xff0000) >> 16; 606 header[4] = (size & 0xff000000) >> 24; 607 *ret = 1; 608 } else { 609 *ret = 0; 610 } 611} 612 613/********************************** 614 ** __gnat_setup_communication () 615 **********************************/ 616 617int 618__gnat_setup_communication (struct TTY_Process** process_out) /* output param */ 619{ 620 struct TTY_Process* process; 621 622 process = (struct TTY_Process*)malloc (sizeof (struct TTY_Process)); 623 ZeroMemory (process, sizeof (struct TTY_Process)); 624 *process_out = process; 625 626 return 0; 627} 628 629#define EXP_PIPE_BASENAME "\\\\.\\pipe\\ExpectPipe" 630 631int 632__gnat_setup_child_communication 633 (struct TTY_Process* process, 634 char** argv, 635 int Use_Pipes) 636{ 637 int cpid; 638 HANDLE parent; 639 SECURITY_ATTRIBUTES sec_attrs; 640 char slavePath [MAX_PATH]; 641 char **nargv; 642 int argc; 643 int i; 644 char pipeNameIn[100]; 645 HANDLE hSlaveInDrv = NULL; /* Handle to communicate with slave driver */ 646 647 parent = GetCurrentProcess (); 648 649 /* Set inheritance for the pipe handles */ 650 sec_attrs.nLength = sizeof (SECURITY_ATTRIBUTES); 651 sec_attrs.bInheritHandle = TRUE; 652 sec_attrs.lpSecurityDescriptor = NULL; 653 654 if (Use_Pipes) { 655 /* Create in and out pipes */ 656 if (!CreatePipe (&process->w_forkin, &process->w_infd, &sec_attrs, 0)) 657 report_file_error ("Creation of child's IN handle", Qnil); 658 if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0)) 659 report_file_error ("Creation of child's OUT handle", Qnil); 660 661 /* Do not inherit the parent's side of the pipes */ 662 SetHandleInformation (&process->w_infd, HANDLE_FLAG_INHERIT, 0); 663 SetHandleInformation (&process->w_outfd, HANDLE_FLAG_INHERIT, 0); 664 665 /* use native argv */ 666 nargv = argv; 667 process->usePipe = TRUE; 668 669 } else { 670 static int pipeNameId = 0; 671 672 process->w_infd = NULL; 673 674 /* We create a named pipe for Input, as we handle input by sending special 675 commands to the explaunch process, that uses it to feed the actual input 676 of the process */ 677 sprintf(pipeNameIn, "%sIn%08x_%08x", EXP_PIPE_BASENAME, 678 GetCurrentProcessId(), pipeNameId); 679 pipeNameId++; 680 681 hSlaveInDrv = CreateNamedPipe(pipeNameIn, 682 PIPE_ACCESS_OUTBOUND, 683 PIPE_TYPE_BYTE | PIPE_WAIT, 1, 8192, 8192, 684 20000, NULL); 685 if (hSlaveInDrv == NULL) goto end; 686 687 if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0)) 688 report_file_error ("Creation of child's OUT handle", Qnil); 689 690 if (SearchPath (NULL, "explaunch.exe", NULL, 691 MAX_PATH, slavePath, NULL) == 0) goto end; 692 693 for (argc=0; argv[argc] != NULL; argc++) ; 694 nargv = (char **) malloc (sizeof (char*) * (argc + 3)); 695 nargv[0] = slavePath; 696 nargv[1] = pipeNameIn; 697 698 for (i = 0; i <= argc; i++) nargv[i + 2] = argv[i]; 699 process->usePipe = FALSE; 700 } 701 702 /* Spawn the child. */ 703 cpid = nt_spawnve (nargv[0], nargv, NULL, process); 704 705 /* close the duplicated handles passed to the child */ 706 CloseHandle (process->w_forkout); 707 708 if (process->usePipe == TRUE) { 709 CloseHandle (process->w_forkin); 710 711 } else { 712 UCHAR buf[8]; /* enough space for child status info */ 713 DWORD count; 714 BOOL bRet; 715 DWORD dwRet; 716 717 /* 718 * Wait for connection with the slave driver 719 */ 720 bRet = ConnectNamedPipe(hSlaveInDrv, NULL); 721 if (bRet == FALSE) { 722 dwRet = GetLastError(); 723 if (dwRet == ERROR_PIPE_CONNECTED) { 724 ; 725 } else { 726 goto end; 727 } 728 } 729 730 process->w_infd = hSlaveInDrv; 731 732 /* 733 * wait for slave driver to initialize before allowing user to send to it 734 */ 735 bRet = ReadFile(process->w_outfd, buf, 8, &count, NULL); 736 if (bRet == FALSE) { 737 cpid = -1; 738 } 739 740 dwRet = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); 741 if (dwRet != 0) { 742 cpid = -1; 743 } 744 745 cpid = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); 746 process->pid = cpid; 747 } 748 749 if (cpid == -1) 750 /* An error occurred while trying to spawn the process. */ 751 report_file_error ("Spawning child process", Qnil); 752 753 return cpid; 754 end: 755 if (hSlaveInDrv != NULL) 756 CloseHandle (hSlaveInDrv); 757 return -1; 758} 759 760void 761__gnat_setup_parent_communication 762 (struct TTY_Process* process, 763 int* in, 764 int* out, 765 int* err, 766 int* pid) 767{ 768 *in = _open_osfhandle ((long) process->w_infd, 0); 769 *out = _open_osfhandle ((long) process->w_outfd, 0); 770 /* child's stderr is always redirected to outfd */ 771 *err = *out; 772 *pid = process->pid; 773} 774 775typedef struct _child_process 776{ 777 HWND hwnd; 778 PROCESS_INFORMATION *procinfo; 779} child_process; 780 781/* The major and minor versions of NT. */ 782static int w32_major_version; 783static int w32_minor_version; 784 785/* Distinguish between Windows NT and Windows 95. */ 786static enum {OS_UNKNOWN, OS_WIN95, OS_NT} os_subtype = OS_UNKNOWN; 787 788/* Cache information describing the NT system for later use. */ 789static void 790cache_system_info (void) 791{ 792 union 793 { 794 struct info 795 { 796 char major; 797 char minor; 798 short platform; 799 } info; 800 DWORD data; 801 } version; 802 803 /* Cache the version of the operating system. */ 804 version.data = GetVersion (); 805 w32_major_version = version.info.major; 806 w32_minor_version = version.info.minor; 807 808 if (version.info.platform & 0x8000) 809 os_subtype = OS_WIN95; 810 else 811 os_subtype = OS_NT; 812} 813 814static BOOL CALLBACK 815find_child_console (HWND hwnd, child_process * cp) 816{ 817 DWORD thread_id; 818 DWORD process_id; 819 820 thread_id = GetWindowThreadProcessId (hwnd, &process_id); 821 if (process_id == cp->procinfo->dwProcessId) 822 { 823 char window_class[32]; 824 825 GetClassName (hwnd, window_class, sizeof (window_class)); 826 if (strcmp (window_class, 827 (os_subtype == OS_WIN95) 828 ? "tty" 829 : "ConsoleWindowClass") == 0) 830 { 831 cp->hwnd = hwnd; 832 return FALSE; 833 } 834 } 835 /* keep looking */ 836 return TRUE; 837} 838 839int 840__gnat_interrupt_process (struct TTY_Process* p) 841{ 842 char buf[2]; 843 DWORD written; 844 BOOL bret; 845 846 if (p->usePipe == TRUE) { 847 bret = FALSE; 848 } else { 849 buf[0] = EXP_SLAVE_KILL; 850 buf[1] = EXP_KILL_CTRL_C; 851 bret = WriteFile (p->w_infd, buf, 2, &written, NULL); 852 } 853 854 if (bret == FALSE) { 855 return __gnat_interrupt_pid (p->procinfo.dwProcessId); 856 } 857 return 0; 858} 859 860int 861__gnat_interrupt_pid (int pid) 862{ 863 volatile child_process cp; 864 int rc = 0; 865 866 cp.procinfo = (LPPROCESS_INFORMATION) malloc (sizeof (PROCESS_INFORMATION)); 867 cp.procinfo->dwProcessId = pid; 868 869 if (os_subtype == OS_UNKNOWN) 870 cache_system_info (); 871 872 /* Try to locate console window for process. */ 873 EnumWindows ((WNDENUMPROC) find_child_console, (LPARAM) &cp); 874 875 if (cp.hwnd) 876 { 877 BYTE control_scan_code = (BYTE) MapVirtualKey (VK_CONTROL, 0); 878 /* Retrieve Ctrl-C scancode */ 879 BYTE vk_break_code = 'C'; 880 BYTE break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0); 881 HWND foreground_window; 882 883 foreground_window = GetForegroundWindow (); 884 if (foreground_window) 885 { 886 /* NT 5.0, and apparently also Windows 98, will not allow 887 a Window to be set to foreground directly without the 888 user's involvement. The workaround is to attach 889 ourselves to the thread that owns the foreground 890 window, since that is the only thread that can set the 891 foreground window. */ 892 DWORD foreground_thread, child_thread; 893 894 foreground_thread = 895 GetWindowThreadProcessId (foreground_window, NULL); 896 if (foreground_thread == GetCurrentThreadId () 897 || !AttachThreadInput (GetCurrentThreadId (), 898 foreground_thread, TRUE)) 899 foreground_thread = 0; 900 901 child_thread = GetWindowThreadProcessId (cp.hwnd, NULL); 902 if (child_thread == GetCurrentThreadId () 903 || !AttachThreadInput (GetCurrentThreadId (), 904 child_thread, TRUE)) 905 child_thread = 0; 906 907 /* Set the foreground window to the child. */ 908 if (SetForegroundWindow (cp.hwnd)) 909 { 910 /* Generate keystrokes as if user had typed Ctrl-Break or 911 Ctrl-C. */ 912 keybd_event (VK_CONTROL, control_scan_code, 0, 0); 913 keybd_event (vk_break_code, break_scan_code, 914 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY), 0); 915 keybd_event (vk_break_code, break_scan_code, 916 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY) 917 | KEYEVENTF_KEYUP, 0); 918 keybd_event (VK_CONTROL, control_scan_code, KEYEVENTF_KEYUP, 0); 919 920 /* Sleep for a bit to give time for the main frame to respond 921 to focus change events. */ 922 Sleep (100); 923 924 SetForegroundWindow (foreground_window); 925 } 926 /* Detach from the foreground and child threads now that 927 the foreground switching is over. */ 928 if (foreground_thread) 929 AttachThreadInput (GetCurrentThreadId (), foreground_thread, FALSE); 930 if (child_thread) 931 AttachThreadInput (GetCurrentThreadId (), child_thread, FALSE); 932 } 933 } 934 /* Ctrl-Break is NT equivalent of SIGINT. */ 935 else if (!GenerateConsoleCtrlEvent 936 (CTRL_BREAK_EVENT, cp.procinfo->dwProcessId)) 937 { 938 errno = EINVAL; 939 rc = -1; 940 } 941 942 free (cp.procinfo); 943 return rc; 944} 945 946/* kill a process, as this implementation use CreateProcess on Win32 we need 947 to use Win32 TerminateProcess API */ 948int 949__gnat_terminate_process (struct TTY_Process* p) 950{ 951 char buf[2]; 952 DWORD written; 953 BOOL bret; 954 955 if (p->usePipe == TRUE) { 956 bret = FALSE; 957 } else { 958 buf[0] = EXP_SLAVE_KILL; 959 buf[1] = EXP_KILL_TERMINATE; 960 bret = WriteFile (p->w_infd, buf, 2, &written, NULL); 961 } 962 963 if (bret == FALSE) { 964 if (!TerminateProcess (p->procinfo.hProcess, 1)) 965 return -1; 966 else 967 return 0; 968 } else 969 return 0; 970} 971 972/* wait for process pid to terminate and return the process status. This 973 implementation is different from the adaint.c one for Windows as it uses 974 the Win32 API instead of the C one. */ 975 976int 977__gnat_tty_waitpid (struct TTY_Process* p) 978{ 979 DWORD exitcode; 980 DWORD res; 981 HANDLE proc_hand = p->procinfo.hProcess; 982 983 res = WaitForSingleObject (proc_hand, 0); 984 GetExitCodeProcess (proc_hand, &exitcode); 985 986 CloseHandle (p->procinfo.hThread); 987 CloseHandle (p->procinfo.hProcess); 988 989 /* No need to close the handles: they were closed on the ada side */ 990 991 return (int) exitcode; 992} 993 994/******************************** 995 ** __gnat_free_process () 996 ********************************/ 997 998void 999__gnat_free_process (struct TTY_Process** process) 1000{ 1001 free (*process); 1002 *process = NULL; 1003} 1004 1005/* TTY handling */ 1006 1007typedef struct { 1008 int tty_fd; /* descriptor for the tty */ 1009 char tty_name[24]; /* Name of TTY device */ 1010} TTY_Handle; 1011 1012int 1013__gnat_tty_supported (void) 1014{ 1015 return 0; 1016} 1017 1018/* Return the tty name associated with p */ 1019 1020char * 1021__gnat_tty_name (TTY_Handle* t) 1022{ 1023 return t->tty_name; 1024} 1025 1026int 1027__gnat_tty_fd (TTY_Handle* t) 1028{ 1029 return t->tty_fd; 1030} 1031 1032TTY_Handle* 1033__gnat_new_tty (void) 1034{ 1035 return (TTY_Handle*)0; 1036} 1037 1038void 1039__gnat_reset_tty (TTY_Handle* t) 1040{ 1041 return; 1042} 1043 1044void 1045__gnat_close_tty (TTY_Handle* t) 1046{ 1047 free (t); 1048} 1049 1050void 1051__gnat_setup_winsize (void *desc, int rows, int columns) 1052{ 1053} 1054 1055#else /* defined(_WIN32, implementatin for all UNIXes */ 1056 1057/* First defined some macro to identify easily some systems */ 1058#if defined (__FreeBSD__) \ 1059 || defined (__OpenBSD__) \ 1060 || defined (__NetBSD__) \ 1061 || defined (__DragonFly__) 1062# define FREEBSD 1063#endif 1064 1065/* Include every system header we need */ 1066#define _GNU_SOURCE 1067#include <errno.h> 1068#include <stdio.h> 1069#include <stdlib.h> 1070 1071/* On some system termio is either absent or including it will disable termios 1072 (HP-UX) */ 1073#if ! defined (__hpux__) && ! defined (FREEBSD) && \ 1074 ! defined (__APPLE__) && ! defined(__rtems__) 1075# include <termio.h> 1076#endif 1077 1078#include <sys/ioctl.h> 1079#include <termios.h> 1080#include <fcntl.h> 1081#include <string.h> 1082#include <sys/stat.h> 1083#include <sys/types.h> 1084#include <sys/wait.h> 1085#include <unistd.h> 1086#if defined (sun) 1087# include <sys/stropts.h> 1088#endif 1089#if defined (FREEBSD) || defined (sun) 1090# include <sys/signal.h> 1091#endif 1092#if defined (__hpux__) 1093# include <sys/termio.h> 1094# include <sys/stropts.h> 1095#endif 1096 1097#define CDISABLE _POSIX_VDISABLE 1098 1099/* On HP-UX and Sun system, there is a bzero function but with a different 1100 signature. Use memset instead */ 1101#if defined (__hpux__) || defined (sun) || defined (_AIX) 1102# define bzero(s,n) memset (s,0,n) 1103#endif 1104 1105/* POSIX does not specify how to open the master side of a terminal.Several 1106 methods are available (system specific): 1107 1- using a cloning device (USE_CLONE_DEVICE) 1108 2- getpt (USE_GETPT) 1109 3- openpty (USE_OPENPTY) 1110 1111 When using the cloning device method, the macro USE_CLONE_DEVICE should 1112 contains a full path to the adequate device. 1113 1114 When a new system is about to be supported, one of the previous macro should 1115 be set otherwise allocate_pty_desc will return an error 1116*/ 1117 1118/* Configurable part */ 1119#if defined (__APPLE__) || defined (FREEBSD) 1120#define USE_OPENPTY 1121#elif defined (linux) 1122#define USE_GETPT 1123#elif defined (sun) 1124#define USE_CLONE_DEVICE "/dev/ptmx" 1125#elif defined (_AIX) 1126#define USE_CLONE_DEVICE "/dev/ptc" 1127#elif defined (__hpux__) 1128/* On HP-UX we use the streamed version. Using the non streamed version is not 1129 recommanded (through "/dev/ptym/clone"). Indeed it seems that there are 1130 issues to detect process terminations. */ 1131#define USE_CLONE_DEVICE "/dev/ptmx" 1132#endif 1133 1134/* structure that holds information about the terminal used and the process 1135 connected on the slave side */ 1136typedef struct pty_desc_struct { 1137 int master_fd; /* fd of the master side if the terminal */ 1138 int slave_fd; /* fd of the slave side */ 1139 char slave_name[32]; /* filename of the slave side */ 1140 int child_pid; /* PID of the child process connected to the slave side 1141 of the terminal */ 1142} pty_desc; 1143 1144/* allocate_pty_desc - allocate a pseudo terminal 1145 * 1146 * PARAMETERS 1147 * out desc returned pointer to a pty_desc structure containing information 1148 * about the opened pseudo terminal 1149 * RETURN VALUE 1150 * -1 if failed 1151 * 0 if ok 1152 * COMMENTS 1153 * If the function is successful we should have at least the master side fd 1154 * and the slave side filename. On some system, the slave side will also be 1155 * opened. If this is not the case the slave side will be open once we are in 1156 * the child process (note that opening the slave side at this stage will 1157 * failed...). 1158 */ 1159 1160extern char* ptsname (int); 1161 1162static int 1163allocate_pty_desc (pty_desc **desc) { 1164 1165 pty_desc *result; 1166 int status = 0; 1167 int slave_fd = -1; 1168 int master_fd = -1; 1169 char *slave_name = NULL; 1170 1171#ifdef USE_GETPT 1172 master_fd = getpt (); 1173#elif defined (USE_OPENPTY) 1174 status = openpty (&master_fd, &slave_fd, NULL, NULL, NULL); 1175#elif defined (USE_CLONE_DEVICE) 1176 master_fd = open (USE_CLONE_DEVICE, O_RDWR | O_NONBLOCK, 0); 1177#else 1178 printf ("[error]: terminal support is not configured\n"); 1179 return -1; 1180#endif 1181 1182 /* at this stage we should have the master side fd and status should be 0 */ 1183 if (status != 0 || master_fd < 0) 1184 { 1185 /* If this is not the case close all opened files and return -1 */ 1186 printf ("[error]: cannot allocate master side of the pty\n"); 1187 if (master_fd >= 0) close (master_fd); 1188 if (slave_fd >= 0) close (slave_fd); 1189 *desc = NULL; 1190 return -1; 1191 } 1192 1193 /* retrieve the file name of the slave side if necessary */ 1194 if (slave_name == NULL) slave_name = (char *) ptsname (master_fd); 1195 1196 /* Now we should have slave file name */ 1197 if (slave_name == NULL) 1198 { 1199 /* If not the case close any opened file and return - 1 */ 1200 printf ("[error]: cannot allocate slave side of the pty\n"); 1201 if (master_fd >= 0) close (master_fd); 1202 if (slave_fd >= 0) close (slave_fd); 1203 *desc = NULL; 1204 return -1; 1205 } 1206 1207#if !defined(__rtems__) 1208 /* grant access to the slave side */ 1209 grantpt (master_fd); 1210 /* unlock the terminal */ 1211 unlockpt (master_fd); 1212#endif 1213 1214 /* set desc and return 0 */ 1215 result = malloc (sizeof (pty_desc)); 1216 result->master_fd = master_fd; 1217 result->slave_fd = slave_fd; 1218 /* the string returned by ptsname or _getpty is a static allocated string. So 1219 we should make a copy */ 1220 strncpy (result->slave_name, slave_name, sizeof (result->slave_name)); 1221 result->slave_name[sizeof (result->slave_name) - 1] = '\0'; 1222 result->child_pid = -1; 1223 *desc=result; 1224 return 0; 1225} 1226 1227/* some utility macro that make the code of child_setup_tty easier to read */ 1228#define __enable(a, b) ((a) |= (b)) 1229#define __disable(a, b) ((a) &= ~(b)) 1230 1231/* some properties do not exist on all systems. Set their value to 0 in that 1232 case */ 1233#ifndef IUCLC 1234#define IUCLC 0 1235#endif 1236#ifndef OLCUC 1237#define OLCUC 0 1238#endif 1239#ifndef NLDLY 1240#define NLDLY 0 1241#define CRDLY 0 1242#define TABDLY 0 1243#define BSDLY 0 1244#define VTDLY 0 1245#define FFDLY 0 1246#endif 1247 1248/* child_setup_tty - set terminal properties 1249 * 1250 * PARAMETERS 1251 * file descriptor of the slave side of the terminal 1252 * 1253 * RETURN VALUE 1254 * 0 if success, any other value if failed. 1255 * 1256 * COMMENTS 1257 * None 1258 */ 1259static int 1260child_setup_tty (int fd) 1261{ 1262 struct termios s; 1263 int status; 1264 1265 /* ensure that s is filled with 0 */ 1266 bzero (&s, sizeof (s)); 1267 1268 /* Get the current terminal settings */ 1269 status = tcgetattr (fd, &s); 1270 if (status != 0) return -1; 1271 1272 /* Adjust input modes */ 1273 __disable (s.c_iflag, IUCLC); /* don't transform to lower case */ 1274 __disable (s.c_iflag, ISTRIP); /* don't delete 8th bit */ 1275 1276 /* Adjust output modes */ 1277 __enable (s.c_oflag, OPOST); /* enable postprocessing */ 1278 __disable (s.c_oflag, ONLCR); /* don't map LF to CR-LF */ 1279 __disable (s.c_oflag, NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY); 1280 /* disable delays */ 1281 __disable (s.c_oflag, OLCUC); /* don't transform to upper case */ 1282 1283 /* Adjust control modes */ 1284 s.c_cflag = (s.c_cflag & ~CSIZE) | CS8; /* Don't strip 8th bit */ 1285 1286 /* Adjust local modes */ 1287 __disable (s.c_lflag, ECHO); /* disable echo */ 1288 __enable (s.c_lflag, ISIG); /* enable signals */ 1289 __enable (s.c_lflag, ICANON); /* erase/kill/eof processing */ 1290 1291 /* Adjust control characters */ 1292 /* IMPORTANT: we need to ensure that Ctrl-C will trigger an interrupt signal 1293 otherwise send_signal_via_characters will fail */ 1294 s.c_cc[VEOF] = 04; /* insure that EOF is Control-D */ 1295 s.c_cc[VERASE] = CDISABLE; /* disable erase processing */ 1296 s.c_cc[VKILL] = CDISABLE; /* disable kill processing */ 1297 s.c_cc[VQUIT] = 28; /* Control-\ */ 1298 s.c_cc[VINTR] = 03; /* Control-C */ 1299 s.c_cc[VEOL] = CDISABLE; 1300 s.c_cc[VSUSP] = 26; /* Control-Z */ 1301 1302 /* push our changes */ 1303 status = tcsetattr (fd, TCSADRAIN, &s); 1304 return status; 1305} 1306 1307/* __gnat_setup_communication - interface to the external world. Should be 1308 * called before forking. On Unixes this function only call allocate_pty_desc. 1309 * The Windows implementation (in different part of this file) is very 1310 * different. 1311 * 1312 * PARAMETERS 1313 * out desc returned pointer to a pty_desc structure 1314 * RETURN VALUE 1315 * 0 if success, -1 otherwise 1316 */ 1317int __gnat_setup_communication (pty_desc** desc) { 1318 return allocate_pty_desc (desc); 1319} 1320 1321/* __gnat_setup_parent_communication - interface to the external world. Should 1322 * be called after forking in the parent process 1323 * 1324 * PARAMETERS 1325 * out in_fd 1326 out out_fd 1327 out err_fd fds corresponding to the parent side of the 1328 terminal 1329 in pid_out child process pid 1330 * RETRUN VALUE 1331 * 0 1332 */ 1333void 1334__gnat_setup_parent_communication 1335 (pty_desc *desc, 1336 int* in_fd, /* input */ 1337 int* out_fd, /* output */ 1338 int* err_fd, /* error */ 1339 int* pid_out) 1340{ 1341 1342 *in_fd = desc->master_fd; 1343 *out_fd= desc->master_fd; 1344 *err_fd= desc->master_fd; 1345 desc->child_pid = *pid_out; 1346} 1347 1348/* __gnat_setup_winsize - Sets up the size of the terminal 1349 * This lets the process know the size of the terminal 1350 */ 1351 1352void __gnat_setup_winsize (pty_desc *desc, int rows, int columns) { 1353#ifdef TIOCGWINSZ 1354 struct winsize s; 1355 s.ws_row = (unsigned short)rows; 1356 s.ws_col = (unsigned short)columns; 1357 s.ws_xpixel = 0; 1358 s.ws_ypixel = 0; 1359 ioctl (desc->master_fd, TIOCSWINSZ, &s); 1360#ifdef SIGWINCH 1361 if (desc->child_pid > 0) { 1362 /* Let the process know about the change in size */ 1363 kill (desc->child_pid, SIGWINCH); 1364 } 1365#endif 1366#endif 1367} 1368 1369/* __gnat_setup_child_communication - interface to external world. Should be 1370 * called after forking in the child process. On Unixes, this function 1371 * first adjust the line setting, set standard output, input and error and 1372 * then spawn the program. 1373 * 1374 * PARAMETERS 1375 * desc a pty_desc structure containing the pty parameters 1376 * new_argv argv of the program to be spawned 1377 * RETURN VALUE 1378 * this function should not return 1379 */ 1380int 1381__gnat_setup_child_communication 1382 (pty_desc *desc, 1383 char **new_argv, 1384 int Use_Pipes) 1385{ 1386 int status; 1387 int pid = getpid (); 1388 1389 setsid (); 1390 1391 /* open the slave side of the terminal if necessary */ 1392 if (desc->slave_fd == -1) 1393#if defined (_AIX) 1394 /* On AIX, if the slave process is not opened with O_NDELAY or O_NONBLOCK 1395 then we might have some processes hanging on I/O system calls. Not sure 1396 we can do that for all platforms so do it only on AIX for the moment. 1397 On AIX O_NONBLOCK and O_NDELAY have slightly different meanings. When 1398 reading on the slave fd, in case there is no data available, if O_NDELAY 1399 is set then 0 is returned. If O_NON_BLOCK is -1 is returned. It seems 1400 that interactive programs such as GDB prefer the O_NDELAY behavior. 1401 We chose O_NONBLOCK because it allows us to make the distinction 1402 between a true EOF and an EOF returned because there is no data 1403 available to be read. */ 1404 desc->slave_fd = open (desc->slave_name, O_RDWR | O_NONBLOCK, 0); 1405#else 1406 desc->slave_fd = open (desc->slave_name, O_RDWR, 0); 1407#endif 1408 1409#if defined (sun) || defined (__hpux__) 1410 /* On systems such as Solaris we are using stream. We need to push the right 1411 "modules" in order to get the expected terminal behaviors. Otherwise 1412 functionalities such as termios are not available. */ 1413 ioctl (desc->slave_fd, I_PUSH, "ptem"); 1414 ioctl (desc->slave_fd, I_PUSH, "ldterm"); 1415 ioctl (desc->slave_fd, I_PUSH, "ttcompat"); 1416#endif 1417 1418#ifdef TIOCSCTTY 1419 /* make the tty the controlling terminal */ 1420 status = ioctl (desc->slave_fd, TIOCSCTTY, 0); 1421#endif 1422 1423 /* adjust tty settings */ 1424 child_setup_tty (desc->slave_fd); 1425 __gnat_setup_winsize (desc, 24, 80); /* To prevent errors in some shells */ 1426 1427 /* stdin, stdout and stderr should be now our tty */ 1428 dup2 (desc->slave_fd, 0); 1429 dup2 (desc->slave_fd, 1); 1430 dup2 (desc->slave_fd, 2); 1431 if (desc->slave_fd > 2) close (desc->slave_fd); 1432 1433 /* adjust process group settings */ 1434 status = setpgid (pid, pid); 1435 status = tcsetpgrp (0, pid); 1436 1437 /* launch the program */ 1438 execvp (new_argv[0], new_argv); 1439 1440 /* return the pid */ 1441 return pid; 1442} 1443 1444/* send_signal_via_characters - Send a characters that will trigger a signal 1445 * in the child process. 1446 * 1447 * PARAMETERS 1448 * desc a pty_desc structure containing terminal information 1449 * int a signal number 1450 * RETURN VALUE 1451 * None 1452 */ 1453static void 1454send_signal_via_characters 1455 (pty_desc *desc, 1456 int signal_number) 1457{ 1458 char ctrl_c = 03; 1459 char ctrl_backslash = 28; 1460 char ctrl_Z = 26; 1461 1462 switch (signal_number) 1463 { 1464 case SIGINT: 1465 write (desc->master_fd, &ctrl_c, 1); return; 1466 case SIGQUIT: 1467 write (desc->master_fd, &ctrl_backslash, 1); return; 1468 case SIGTSTP: 1469 write (desc->master_fd, &ctrl_Z, 1); return; 1470 } 1471} 1472 1473/* __gnat_interrupt_process - interrupt the child process 1474 * 1475 * PARAMETERS 1476 * desc a pty_desc structure 1477 */ 1478int 1479__gnat_interrupt_process (pty_desc *desc) 1480{ 1481 send_signal_via_characters (desc, SIGINT); 1482 return 0; 1483} 1484 1485/* __gnat_interrupt_pid - interrupt a process group 1486 * 1487 * PARAMETERS 1488 * pid pid of the process to interrupt 1489 */ 1490int 1491__gnat_interrupt_pid (int pid) 1492{ 1493 kill (-pid, SIGINT); 1494 return 0; 1495} 1496 1497/* __gnat_terminate_process - kill a child process 1498 * 1499 * PARAMETERS 1500 * desc pty_desc structure 1501 */ 1502int __gnat_terminate_process (pty_desc *desc) 1503{ 1504 return kill (desc->child_pid, SIGKILL); 1505} 1506 1507/* __gnat_tty_waitpid - wait for the child process to die 1508 * 1509 * PARAMETERS 1510 * desc pty_desc structure 1511 * RETURN VALUE 1512 * exit status of the child process 1513 */ 1514int 1515__gnat_tty_waitpid (pty_desc *desc) 1516{ 1517 int status = 0; 1518 waitpid (desc->child_pid, &status, 0); 1519 return WEXITSTATUS (status); 1520} 1521 1522/* __gnat_tty_supported - Are tty supported ? 1523 * 1524 * RETURN VALUE 1525 * always 1 on Unix systems 1526 */ 1527int 1528__gnat_tty_supported (void) 1529{ 1530 return 1; 1531} 1532 1533/* __gnat_free_process - free a pty_desc structure 1534 * 1535 * PARAMETERS 1536 * in out desc: a pty desc structure 1537 */ 1538void 1539__gnat_free_process (pty_desc** desc) 1540{ 1541 free (*desc); 1542 *desc = NULL; 1543} 1544 1545/* __gnat_send_header - dummy function. this interface is only used on Windows */ 1546void 1547__gnat_send_header (pty_desc* desc, char header[5], int size, int *ret) 1548{ 1549 *ret = 0; 1550} 1551 1552/* __gnat_reset_tty - reset line setting 1553 * 1554 * PARAMETERS 1555 * desc: a pty_desc structure 1556 */ 1557void 1558__gnat_reset_tty (pty_desc* desc) 1559{ 1560 child_setup_tty (desc->master_fd); 1561} 1562 1563/* __gnat_new_tty - allocate a new terminal 1564 * 1565 * RETURN VALUE 1566 * a pty_desc structure 1567 */ 1568pty_desc * 1569__gnat_new_tty (void) 1570{ 1571 int status; 1572 pty_desc* desc; 1573 status = allocate_pty_desc (&desc); 1574 child_setup_tty (desc->master_fd); 1575 return desc; 1576} 1577 1578/* __gnat_close_tty - close a terminal 1579 * 1580 * PARAMETERS 1581 * desc a pty_desc strucure 1582 */ 1583void __gnat_close_tty (pty_desc* desc) 1584{ 1585 if (desc->master_fd >= 0) close (desc->master_fd); 1586 if (desc->slave_fd >= 0) close (desc->slave_fd); 1587} 1588 1589/* __gnat_tty_name - return slave side device name 1590 * 1591 * PARAMETERS 1592 * desc a pty_desc strucure 1593 * RETURN VALUE 1594 * a string 1595 */ 1596char * 1597__gnat_tty_name (pty_desc* desc) 1598{ 1599 return desc->slave_name; 1600} 1601 1602/* __gnat_tty_name - return master side fd 1603 * 1604 * PARAMETERS 1605 * desc a pty_desc strucure 1606 * RETURN VALUE 1607 * a fd 1608 */ 1609int 1610__gnat_tty_fd (pty_desc* desc) 1611{ 1612 return desc->master_fd; 1613} 1614 1615#endif /* WIN32 */ 1616