Diff for /parser3/src/main/pa_exec.C between versions 1.78 and 1.102

version 1.78, 2009/10/02 01:18:27 version 1.102, 2024/11/25 23:40:40
Line 1 Line 1
 /** @file  /** @file
         Parser: program executing for different OS-es.          Parser: program executing for different OS-es.
   
         Copyright(c) 2000-2009 ArtLebedev Group(http://www.artlebedev.com)          Copyright (c) 2001-2024 Art. Lebedev Studio (http://www.artlebedev.com)
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru>
   
         @todo setrlimit          @todo setrlimit
 */  */
   
 static const char * const IDENT_EXEC_C="$Date$";  
   
 #include "pa_config_includes.h"  #include "pa_config_includes.h"
   
 #include "pa_exec.h"  #include "pa_exec.h"
 #include "pa_exception.h"  #include "pa_exception.h"
 #include "pa_common.h"  #include "pa_common.h"
   
 #ifdef WIN32  volatile const char * IDENT_PA_EXEC_C="$Id$" IDENT_PA_EXEC_H;
 #       include <windows.h>  
 #else  
 #       include <signal.h>  
 #       include <sys/types.h>  
 #       include <sys/wait.h>  
 #endif  
   
 #ifdef WIN32  #ifdef _MSC_VER
   
   #include <windows.h>
   
 /// this func from http://www.ccas.ru/~posp/popov/spawn.htm  /// this func from http://www.ccas.ru/~posp/popov/spawn.htm
 static DWORD CreateHiddenConsoleProcess(LPCTSTR szCmdLine,  static DWORD CreateHiddenConsoleProcess(LPCTSTR szCmdLine,
                                                                                 LPCTSTR szScriptFileSpec,                                          LPCTSTR szScriptFileSpec,
                                                                                 char *szEnv,                                          char *szEnv,
                                                                                 PROCESS_INFORMATION* ppi,                                           PROCESS_INFORMATION* ppi, 
                                                                                 LPHANDLE phInWrite,                                          LPHANDLE phInWrite,
                                                                                 LPHANDLE phOutRead,                                          LPHANDLE phOutRead,
                                                                                 LPHANDLE phErrRead)                                          LPHANDLE phErrRead)
 {  {
         DWORD result=0;          DWORD result=0;
         BOOL fCreated;          BOOL fCreated;
Line 51  static DWORD CreateHiddenConsoleProcess( Line 45  static DWORD CreateHiddenConsoleProcess(
         // create STDIN pipe          // create STDIN pipe
         if(!CreatePipe(&hInRead, phInWrite, &sa, 0))          if(!CreatePipe(&hInRead, phInWrite, &sa, 0))
                 goto error;                  goto error;
           
           // Ensure the write handle to the pipe for STDIN is not inherited. 
           if (!SetHandleInformation(*phInWrite, HANDLE_FLAG_INHERIT, 0))
                   goto error;
   
         // create STDOUT pipe          // create STDOUT pipe
         if(!CreatePipe(phOutRead, &hOutWrite, &sa, 0))          if(!CreatePipe(phOutRead, &hOutWrite, &sa, 0))
                 goto error;                  goto error;
                   
           // Ensure the read handle to the pipe for STDOUT is not inherited.
           if (!SetHandleInformation(*phOutRead, HANDLE_FLAG_INHERIT, 0))
                   goto error;
   
         // create STDERR pipe          // create STDERR pipe
         if(!CreatePipe(phErrRead, &hErrWrite, &sa, 0))          if(!CreatePipe(phErrRead, &hErrWrite, &sa, 0))
                 goto error;                  goto error;
                   
           // Ensure the read handle to the pipe for STDERR is not inherited.
           if (!SetHandleInformation(*phErrRead, HANDLE_FLAG_INHERIT, 0))
                   goto error;
   
         // process startup information          // process startup information
         memset(&si, 0, sizeof(si));          memset(&si, 0, sizeof(si));
         si.cb=sizeof(si);           si.cb=sizeof(si); 
Line 73  static DWORD CreateHiddenConsoleProcess( Line 79  static DWORD CreateHiddenConsoleProcess(
                   
         // calculating script's directory          // calculating script's directory
         char dir[MAX_STRING];          char dir[MAX_STRING];
         strncpy(dir, szScriptFileSpec, MAX_STRING-1); dir[MAX_STRING-1]=0;          pa_strncpy(dir, szScriptFileSpec, MAX_STRING);
         lsplit(dir,' '); // trim arguments          lsplit(dir,' '); // trim arguments
         rsplit(dir,'/'); rsplit(dir,'\\'); // trim filename          rsplit(dir,'/'); rsplit(dir,'\\'); // trim filename
                   
Line 111  error: Line 117  error:
         return result;          return result;
 }  }
   
   static int get_exit_status(HANDLE hProcess) {
           DWORD dwExitCode = 0;
           if(!GetExitCodeProcess(hProcess, &dwExitCode))
                   return -1;
           if(dwExitCode != STILL_ACTIVE)
                   return dwExitCode;
           // wait for 1 second for process to exit
           if(WaitForSingleObject(hProcess, 1000) != WAIT_OBJECT_0)
                   return -2;
           if(!GetExitCodeProcess(hProcess, &dwExitCode))
                   return -1;
           return dwExitCode;
   }
   
 static void read_pipe(String& result, HANDLE hOutRead, String::Language lang){  static void read_pipe(String& result, HANDLE hOutRead, String::Language lang){
         while(true) {          while(true) {
                 char *buf=new(PointerFreeGC) char[MAX_STRING+1];                  char *buf=new(PointerFreeGC) char[MAX_STRING+1];
Line 145  static void read_pipe(File_read_result& Line 165  static void read_pipe(File_read_result&
         }          }
 }  }
   
   static const char* arg_quote(const char *arg) {
           size_t length = strlen(arg);
           if (length >= 2 && arg[0] == '"' && arg[length - 1] == '"')
                   return arg; // allready qouted
   
           size_t extra_length = 2; // opening and closing quotes
           for(const char *src = arg; *src; src++){
                   if(*src == '"' || *src == '\\')
                           extra_length++;
           }
   
           char *result = (char *)pa_malloc(length + extra_length + 1);
           char *dest = result;
   
           *dest++ = '"';
   
           for(const char *src=arg; *src;){
                   char c = *src++;
                   if(c == '"'){
                           *dest++ = '"'; // required for .cmd, .exe also supports \"
                   } else if(c == '\\'){
                           *dest++ = '\\';
                   }
                   *dest++ = c;
           }
   
           *dest++ = '"';
           *dest = '\0';
           return result;
   }
   
 static const char* buildCommand(const char* file_spec_cstr, const ArrayString& argv) {  static const char* buildCommand(const char* file_spec_cstr, const ArrayString& argv) {
         const char* result=file_spec_cstr;          const char* result=file_spec_cstr;
         if(FILE *f=fopen(file_spec_cstr, "r")) {          if(FILE *f=pa_fopen(file_spec_cstr, "r")) {
                 try {                  try {
                 char buf[MAX_STRING];                  char buf[MAX_STRING];
                 size_t size=fread(buf, 1, MAX_STRING-1, f);                  size_t size=fread(buf, 1, MAX_STRING-1, f);
Line 170  static const char* buildCommand(const ch Line 221  static const char* buildCommand(const ch
                 }                  }
                 fclose(f);                  fclose(f);
         }          }
   
         { // appending argv          { // appending argv
                 String string(result);                  String string(result);
                 for(size_t i=0; i<argv.count(); i++) {                  for(size_t i=0; i<argv.count(); i++) {
                         string << " ";                          string << " ";
                         string << *argv[i];                          string << arg_quote(argv[i]->cstr());
                 }                  }
   
                 result=string.cstr();                  result=string.cstr();
         }          }
   
Line 263  static pid_t execve_piped(const char* fi Line 314  static pid_t execve_piped(const char* fi
                   
                 // chdir to script's directory                  // chdir to script's directory
                 char dir[MAX_STRING];                  char dir[MAX_STRING];
                 strncpy(dir, file_spec_cstr, MAX_STRING-1); dir[MAX_STRING-1]=0;                  pa_strncpy(dir, file_spec_cstr, MAX_STRING);
                 rsplit(dir,'/'); // trim filename                  rsplit(dir,'/'); // trim filename
                 chdir(dir);                  chdir(dir);
   
Line 293  static pid_t execve_piped(const char* fi Line 344  static pid_t execve_piped(const char* fi
 }  }
   
 static int get_exit_status(int pid) {  static int get_exit_status(int pid) {
         int status;          int status=0;
         pid_t cid;          pid_t cid;
         while ((cid=waitpid(pid, &status, WUNTRACED)) == -1 && errno == EINTR);           while ((cid=waitpid(pid, &status, WUNTRACED)) == -1 && errno == EINTR); 
         if(!cid)          if(!cid)
Line 315  static void read_pipe(String& result, in Line 366  static void read_pipe(String& result, in
   
 static void read_pipe(File_read_result& result, int file){  static void read_pipe(File_read_result& result, int file){
         char *buf=(char*)pa_malloc(MAX_STRING+1);          char *buf=(char*)pa_malloc(MAX_STRING+1);
         ssize_t bufsize = MAX_STRING;          size_t bufsize = MAX_STRING;
   
         result.headers = 0;          result.headers = 0;
         result.length = 0;          result.length = 0;
Line 339  static void read_pipe(File_read_result& Line 390  static void read_pipe(File_read_result&
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct Append_env_pair_info {  struct Append_env_pair_info {
 #ifdef WIN32  #ifdef _MSC_VER
         String::Body& body;          String::Body& body;
         Append_env_pair_info(String::Body& abody): body(abody) {}          Append_env_pair_info(String::Body& abody): body(abody) {}
 #else  #else
Line 347  struct Append_env_pair_info { Line 398  struct Append_env_pair_info {
 #endif  #endif
 };  };
 #endif  #endif
 ///@test maybe here and at argv construction --- untaint_cstr(String::L_AS_IS  
 static void append_env_pair(HashStringString::key_type key, HashStringString::value_type value,  static void append_env_pair(HashStringString::key_type key, HashStringString::value_type value,
                 Append_env_pair_info *info) {                  Append_env_pair_info *info) {
 #ifdef WIN32  #ifdef _MSC_VER
         info->body << key << "=" << value;          info->body << key << "=" << value;
         info->body.append_know_length("\1", 1); // placeholder for of zero byte          info->body.append_know_length("\1", 1); // placeholder for of zero byte
 #else  #else
Line 361  static void append_env_pair(HashStringSt Line 412  static void append_env_pair(HashStringSt
 #endif  #endif
 }  }
   
 PA_exec_result pa_exec(  PA_exec_result pa_exec(bool forced_allow, const String& file_spec, const HashStringString* env, const ArrayString& argv, String::C in) {
                         bool   
 #if defined(NO_PA_EXEC) || defined(PA_SAFE_MODE)  
                         forced_allow  
 #endif  
                         ,   
                         const String& file_spec,   
                         const HashStringString* env,   
                         const ArrayString& argv,   
                         String& in) {  
         PA_exec_result result;          PA_exec_result result;
   
 #ifdef NO_PA_EXECS  #ifdef NO_PA_EXECS
         if(!forced_allow)          if(!forced_allow)
                 throw Exception(PARSER_RUNTIME,                  throw Exception(PARSER_RUNTIME, &file_spec, "parser execs are disabled [recompile parser without --disable-execs configure option]");
                         &file_spec,  
                         "parser execs are disabled [recompile parser without --disable-execs configure option]");  
 #endif  #endif
   
 #ifdef WIN32  #ifdef _MSC_VER
   
         PROCESS_INFORMATION pi;           PROCESS_INFORMATION pi; 
         HANDLE hInWrite, hOutRead, hErrRead;          HANDLE hInWrite, hOutRead, hErrRead;
Line 406  PA_exec_result pa_exec( Line 446  PA_exec_result pa_exec(
                 if(error_size>3) // ".\r\n"                  if(error_size>3) // ".\r\n"
                         szErrorDesc[error_size-3]=0;                          szErrorDesc[error_size-3]=0;
   
                 throw Exception("file.execute",                  throw Exception("file.execute", &file_spec, "exec failed - %s (%u). Consider adding shbang line (#!x:\\interpreter\\command line)", error_size ? szErrorDesc : "<unknown>", error);
                         &file_spec,  
                         "exec failed - %s (%u). Consider adding shbang line (#!x:\\interpreter\\command line)",   
                                 error_size?szErrorDesc:"<unknown>", error);  
         } else {          } else {
                 const char* in_cstr=in.cstr();  
                 DWORD written_size;                  DWORD written_size;
                 WriteFile(hInWrite, in_cstr, in.length(), &written_size, NULL);                  if(in.length>0)
                 // EOF for stupid text reads                          WriteFile(hInWrite, in.str, in.length, &written_size, NULL);
                 // normally they should read CONTENT_LENGTH bytes,  
                 // without this char  
                 WriteFile(hInWrite, "\x1A", 1, &written_size, NULL);  
                 CloseHandle(hInWrite);                  CloseHandle(hInWrite);
                 read_pipe(result.out, hOutRead);                  read_pipe(result.out, hOutRead);
                 CloseHandle(hOutRead);                  CloseHandle(hOutRead);
                 read_pipe(result.err, hErrRead, String::L_TAINTED);                               read_pipe(result.err, hErrRead, String::L_TAINTED);
                 CloseHandle(hErrRead);                  CloseHandle(hErrRead);
 /*                        result.status=get_exit_status(pi.hProcess);
 from http://www.apache.org/websrc/cvsweb.cgi/apache-1.3/src/main/util_script.c?rev=1.151&content-type=text/vnd.viewcvs-markup                  // We must close the handles to the new process and its main thread
                   // to prevent handle and memory leaks.
         * We must close the handles to the new process and its main thread  
         * to prevent handle and memory leaks.  
 */        
                 CloseHandle(pi.hProcess);                  CloseHandle(pi.hProcess);
                 CloseHandle(pi.hThread);                  CloseHandle(pi.hThread);
         }          }
Line 440  from http://www.apache.org/websrc/cvsweb Line 470  from http://www.apache.org/websrc/cvsweb
   
         int pipe_write, pipe_read, pipe_err;          int pipe_write, pipe_read, pipe_err;
   
 #ifdef PA_SAFE_MODE  
         if(!forced_allow) {          if(!forced_allow) {
                 struct stat finfo;                  struct stat finfo;
                 if(stat(file_spec_cstr, &finfo)!=0)                  if(pa_stat(file_spec_cstr, &finfo)!=0)
                         throw Exception("file.missing",                           throw Exception("file.missing", &file_spec, "stat failed: %s (%d), actual filename '%s'", strerror(errno), errno, file_spec_cstr);
                                 &file_spec,   
                                 "stat failed: %s (%d), actual filename '%s'",   
                                         strerror(errno), errno, file_spec_cstr);  
   
                 check_safe_mode(finfo, file_spec, file_spec_cstr);                  check_safe_mode(finfo, file_spec, file_spec_cstr);
         }          }
 #endif  
   
         char* argv_cstrs[1+100+1]={file_spec_cstr, 0};          char* argv_cstrs[1+100+1]={file_spec_cstr, 0};
         const int argv_size=argv.count();          const int argv_size=argv.count();
         const int argv_max=sizeof(argv_cstrs)/sizeof(argv_cstrs[0])-1-1;          const int argv_max=sizeof(argv_cstrs)/sizeof(argv_cstrs[0])-1-1;
         if(argv_size>argv_max)          if(argv_size>argv_max)
                 throw Exception(PARSER_RUNTIME,                  throw Exception(PARSER_RUNTIME, &file_spec, "too many arguments (%d > max %d)", argv_size, argv_max);
                         &file_spec,  
                         "too many arguments (%d > max %d)", argv_size, argv_max);  
         for(int i=0; i<argv_size; i++)          for(int i=0; i<argv_size; i++)
                 argv_cstrs[1+i]=argv[i]->cstrm();                  argv_cstrs[1+i]=argv[i]->cstrm();
         argv_cstrs[1+argv_size]=0;          argv_cstrs[1+argv_size]=0;
Line 479  from http://www.apache.org/websrc/cvsweb Line 502  from http://www.apache.org/websrc/cvsweb
                 &pipe_write, &pipe_read, &pipe_err);                  &pipe_write, &pipe_read, &pipe_err);
         if(pid>0) {          if(pid>0) {
                 // in child                  // in child
                 if(!in.is_empty()) {// there is some in data                  if(in.length>0) // there is some in data
                         const char* in_cstr=in.cstr();                          write(pipe_write, in.str, in.length);
                         write(pipe_write, in_cstr, in.length());  
                 }  
                 close(pipe_write);                  close(pipe_write);
                 read_pipe(result.out, pipe_read);                  read_pipe(result.out, pipe_read);
                 close(pipe_read);                  close(pipe_read);
Line 492  from http://www.apache.org/websrc/cvsweb Line 513  from http://www.apache.org/websrc/cvsweb
                 result.status=get_exit_status(pid); // negative may mean "-errno[execl()]"                  result.status=get_exit_status(pid); // negative may mean "-errno[execl()]"
         } else {           } else { 
                 const char* str=strerror(errno);                  const char* str=strerror(errno);
                 throw Exception("file.execute",                  throw Exception("file.execute", &file_spec, "%s error: %s (%d)", pid<0?"fork":"pipe", str?str:"<unknown>", errno); 
                         &file_spec,  
                         "%s error: %s (%d)", pid<0?"fork":"pipe", str?str:"<unknown>", errno);   
         }          }
 #endif  #endif
   

Removed from v.1.78  
changed lines
  Added in v.1.102


E-mail: