Diff for /parser3/src/main/pa_exec.C between versions 1.48.2.18.2.16 and 1.49

version 1.48.2.18.2.16, 2003/04/08 16:11:54 version 1.49, 2003/04/04 14:42:38
Line 1 Line 1
 /** @file  /** @file
         Parser: program executing for different OS-es.          Parser: program executing for different OS-es.
   
         Copyright(c) 2000,2001-2003 ArtLebedev Group(http://www.artlebedev.com)          Copyright (c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com)
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
   
           portions by Victor Fedoseev" <vvf_ru@mail.ru> [January 23, 2003]
   
         @todo setrlimit          @todo setrlimit
 */  */
   
Line 27  static const char* IDENT_EXEC_C="$Date$" Line 29  static const char* IDENT_EXEC_C="$Date$"
   
 /// 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,
                                                                                 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;
         STARTUPINFO si;      STARTUPINFO si;
         SECURITY_ATTRIBUTES sa={0};      SECURITY_ATTRIBUTES sa={0};
         HANDLE hInRead;      HANDLE hInRead;
         HANDLE hOutWrite;      HANDLE hOutWrite;
         HANDLE hErrWrite;      HANDLE hErrWrite;
           
         // Create pipes      // Create pipes
         // initialize security attributes for handle inheritance (for WinNT)      // initialize security attributes for handle inheritance (for WinNT)
         sa.nLength=sizeof(sa);      sa.nLength=sizeof(sa);
         sa.bInheritHandle=TRUE;      sa.bInheritHandle=TRUE;
         sa.lpSecurityDescriptor=NULL;      sa.lpSecurityDescriptor=NULL;
           
         // create STDIN pipe      // create STDIN pipe
         if(!CreatePipe(&hInRead, phInWrite, &sa, 0))      if(!CreatePipe(&hInRead, phInWrite, &sa, 0))
                 goto error;          goto error;
           
         // create STDOUT pipe      // create STDOUT pipe
         if(!CreatePipe(phOutRead, &hOutWrite, &sa, 0))      if(!CreatePipe(phOutRead, &hOutWrite, &sa, 0))
                 goto error;          goto error;
           
         // create STDERR pipe      // create STDERR pipe
         if(!CreatePipe(phErrRead, &hErrWrite, &sa, 0))      if(!CreatePipe(phErrRead, &hErrWrite, &sa, 0))
                 goto error;          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); 
         si.dwFlags=STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;      si.dwFlags=STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
         // child process' console must be hidden for Win95 compatibility      // child process' console must be hidden for Win95 compatibility
         si.wShowWindow=SW_HIDE;      si.wShowWindow=SW_HIDE;
         // assign "other" sides of pipes      // assign "other" sides of pipes
         si.hStdInput=hInRead;      si.hStdInput=hInRead;
         si.hStdOutput=hOutWrite;      si.hStdOutput=hOutWrite;
         si.hStdError=hErrWrite;      si.hStdError=hErrWrite;
           
         // calculating script's directory          // calculating script's directory
         char dir[MAX_STRING];          char dir[MAX_STRING];
         strncpy(dir, szCmdLine, MAX_STRING-1); dir[MAX_STRING-1]=0;          strncpy(dir, szCmdLine, MAX_STRING-1); dir[MAX_STRING-1]=0;
         lsplit(dir,' '); // trim arguments          lsplit(dir,' '); // trim arguments
         rsplit(dir,'/'); rsplit(dir,'\\'); // trim filename          rsplit(dir,'/'); rsplit(dir,'\\'); // trim filename
         chdir(dir);          chdir(dir);
           
         // Create a child process (suspended)      // Create a child process (suspended)
         fCreated=CreateProcess(NULL,      fCreated=CreateProcess(NULL,
                 (LPTSTR)szCmdLine,                                (LPTSTR)szCmdLine,
                 NULL,                                NULL,
                 NULL,                                NULL,
                 TRUE,                                TRUE,
                 CREATE_NO_WINDOW,                                CREATE_NO_WINDOW,
                 szEnv,                                szEnv,
                 dir,                                dir,
                 &si,                                &si,
                 ppi);                                ppi);
         if(!fCreated)          if(!fCreated)
                 result=GetLastError();                  result=GetLastError();
           
         CloseHandle(hInRead);      CloseHandle(hInRead);
         CloseHandle(hOutWrite);      CloseHandle(hOutWrite);
         CloseHandle(hErrWrite);      CloseHandle(hErrWrite);
           
         if(!fCreated)      if(!fCreated)
                 goto error;          goto error;
           
         return result;      return result;
           
 error:  error:
         if(!result/*yet*/)          if(!result/*yet*/)
                 result=GetLastError(); // get it                  result=GetLastError(); // get it
           
         CloseHandle(*phInWrite);  
         CloseHandle(*phOutRead);  
         CloseHandle(*phErrRead);  
           
         return result;  
 }  
   
 static void read_pipe(String& result, HANDLE hOutRead, String::Language lang){      CloseHandle(*phInWrite);
         while(true) {      CloseHandle(*phOutRead);
                 char *buf=new(PointerFreeGC) char[MAX_STRING];      CloseHandle(*phErrRead);
                 unsigned long size;  
                 if(!ReadFile(hOutRead, buf, MAX_STRING-1, &size, NULL) || !size)       return result;
                         break;  
                 buf[size]=0;  
                 result.append_know_length(buf, size, lang);  
         }  
 }  }
   
 static const char* buildCommand(const char* file_spec_cstr, const ArrayString& argv) {  static const char *buildCommand(Pool& pool, 
         const char* result=file_spec_cstr;                                                                  const String& origin_string,
                                                                   const char *file_spec_cstr, const Array *argv) {
           const char *result=file_spec_cstr;
         if(FILE *f=fopen(file_spec_cstr, "r")) {          if(FILE *f=fopen(file_spec_cstr, "r")) {
                 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);
                 if(size>2) {                  if(size>2) {
                         buf[size]=0;                          buf[size]=0;
                         if(strncmp(buf, "#!", 2)==0) {                          if(strncmp(buf, "#!", 2)==0) {
                                 const char* begin=buf+2;                                  const char *begin=buf+2;
                                 if(*begin==' ') // alx: were an old magic for some linux-es                                  if(*begin==' ') // alx: were an old magic for some linux-es
                                         begin++;                                          begin++;
                                 if(char *end=strchr(begin, '\n')) {                                  if(char *end=strchr(begin, '\n')) {
                                         String string(begin, end-begin);                                          String string(pool);
                                           string.APPEND_AS_IS(begin, end-begin, 
                                                   origin_string.origin().file, 0);
                                         string << " " << file_spec_cstr;                                          string << " " << file_spec_cstr;
                                         result=string.cstr();                                          result=string.cstr();
                                 }                                  }
                         }                          }
                 }                  }
                 } catch(...) {  
                         fclose(f);  
                         rethrow;  
                 }  
                 fclose(f);                  fclose(f);
         }          }
         { // appending argv          if(argv) {
                 String string(result);                  String buf(pool);
                 for(size_t i=0; i<argv.count(); i++) {                  buf << result;
                         string << " ";                  for(int i=0; i<argv->size(); i++) {
                         string << *argv[i];                          buf << " ";
                           buf << *argv->get_string(i);
                 }                  }
   
                 result=string.cstr();                  result=buf.cstr();
         }          }
   
         return result;          return result;
 }  }
   
 #else  #else // WIN32
   
 static pid_t execve_piped(const char* file_spec_cstr,   static pid_t execve_piped(const char *file_spec_cstr, 
                         char * const argv[], char * const env[],                          char * const argv[], char * const env[],
                         int *pipe_in, int *pipe_out, int *pipe_err) {                          int *pipe_in, int *pipe_out, int *pipe_err) {
         pid_t pid;          pid_t pid;
Line 277  static int get_exit_status(int pid) { Line 268  static int get_exit_status(int pid) {
                 WEXITSTATUS(status) : -2;                  WEXITSTATUS(status) : -2;
 }  }
   
 static void read_pipe(String& result, int file, String::Language lang){  #endif // WIN32
   
   static void read_pipe(String& result,
   #ifdef WIN32
                                             HANDLE hOutRead,
   #else // WIN32
                                              int file,
   #endif // WIN32
                                             const char *file_spec, 
                                             String::Untaint_lang lang,
                                             int *header_pos=0, const char **eol_marker=0, size_t *eol_marker_size=0){
   
           if(header_pos) *header_pos = -1;
           if(eol_marker) *eol_marker = "";
           if(eol_marker_size) *eol_marker_size = 0;
   
           int dos_seq_match = 0;
           int unix_seq_match = 0;
           int blk_pos = 0;
           bool do_search = (header_pos || eol_marker || eol_marker_size);
   
         while(true) {          while(true) {
                 char *buf=new(PointerFreeGC) char[MAX_STRING];                  char *buf=(char *)result.pool().malloc(MAX_STRING);
                 ssize_t length=read(file, buf, MAX_STRING-1);  #ifdef WIN32
                 if(length<=0)                  unsigned long size, i;
                   if(!ReadFile(hOutRead, buf, MAX_STRING, &size, NULL) || !size) 
                         break;                          break;
                 buf[length]=0;  #else // WIN32
                 result.append_know_length(buf, length, lang);                  ssize_t i, size=read(file, buf, MAX_STRING);
         }                  if(size<=0)
                           break;
   #endif // WIN32
                   if(do_search){
                           for(i=0; i<size; ++i){
                                   if(buf[i] == '\n'){
                                           if(++unix_seq_match==2){
                                                   do_search = false;
                                                   if(header_pos) *header_pos = blk_pos+i-1;
                                                   if(eol_marker) *eol_marker = "\n";
                                                   if(eol_marker_size) *eol_marker_size = 1;
                                                   break;
                                           }
                                           if(dos_seq_match==1){
                                                   ++dos_seq_match;
                                           }else if(dos_seq_match==3){
                                                   do_search = false;
                                                   if(header_pos) *header_pos = blk_pos+i-3;
                                                   if(eol_marker) *eol_marker = "\r\n";
                                                   if(eol_marker_size) *eol_marker_size = 2;
                                                   break;
                                           }else
                                                   dos_seq_match = 0;
                                   }else if(buf[i] == '\r'){
                                           unix_seq_match = 0;
                                           if(dos_seq_match==0 || dos_seq_match==2)
                                                   ++dos_seq_match;
                                           else
                                                   dos_seq_match = 0;
                                   }else if(buf[i] == 0){
                                           // assuming that the header may not contain 0
                                           do_search = false;
                                           break;
                                   }else{
                                           dos_seq_match = 0;
                                           unix_seq_match = 0;
                                   }
                           }
                           blk_pos += size;
                   }
                   result.APPEND(buf, size, lang, file_spec, 0);
       }
 }  }
   
 #endif  
   
 #ifndef DOXYGEN  ///@test maybe here and at argv construction --- cstr(String::UL_UNSPECIFIED
 struct Append_env_pair_info {  static void append_env_pair(const Hash::Key& key, Hash::Val *value, void *info) {
 #ifdef WIN32  
         String& string;  
         Append_env_pair_info(String& astring): string(astring) {}  
 #else  
         char **env_ref;  
 #endif  
 };  
 #endif  
 ///@test maybe here and at argv construction --- cstr(String::L_UNSPECIFIED  
 static void append_env_pair(HashStringString::key_type key, HashStringString::value_type value,  
                 Append_env_pair_info *info) {  
 #ifdef WIN32  #ifdef WIN32
         info->string << key << "=" << value;          String& string=*static_cast<String *>(info);
         info->string.append_know_length("", 1, String::L_AS_IS); // zero byte          
           string << key << "=" << *static_cast<String *>(value);
           string.APPEND_AS_IS("", 1, 0, 0); // zero byte
 #else  #else
         StringBody body;          String string(key.pool());
         body << key << "=" << value;          string << key << "=" << *static_cast<String *>(value);
   
         *(info->env_ref++)=body.cstrm();          char ***env_ref=static_cast<char ***>(info);
           **env_ref=string.cstr(String::UL_UNSPECIFIED);  (*env_ref)++;
 #endif  #endif
 }  }
   
 /// @todonow unix part to smart_ptr  int pa_exec(
 PA_exec_result pa_exec(                          bool forced_allow,
                         bool forced_allow,   
                         const String& file_spec,                           const String& file_spec, 
                         const HashStringString* env,                           const Hash *env,
                         const ArrayString& argv,                           const Array *argv,
                         String& in) {                          const String& in, String& out, String& err,
         PA_exec_result result;                          int *header_pos, const char **eol_marker, size_t *eol_marker_size) {
           Pool& pool=file_spec.pool();
   
 #ifdef NO_PA_EXECS  #ifdef NO_PA_EXECS
         if(!forced_allow)          if(!forced_allow)
Line 330  PA_exec_result pa_exec( Line 374  PA_exec_result pa_exec(
                         "parser execs are disabled [recompile parser without --disable-execs configure option]");                          "parser execs are disabled [recompile parser without --disable-execs configure option]");
 #endif  #endif
   
         // execve needs non const  
         char* file_spec_cstr=file_spec.cstrm(String::L_FILE_SPEC);   
 #ifdef WIN32  #ifdef WIN32
   
         PROCESS_INFORMATION pi;           PROCESS_INFORMATION pi; 
         HANDLE hInWrite, hOutRead, hErrRead;          HANDLE hInWrite, hOutRead, hErrRead;
         const char* cmd=buildCommand(file_spec.cstr(String::L_FILE_SPEC), argv);          char *file_spec_cstr=file_spec.cstr(String::UL_FILE_SPEC); 
         char* env_cstr;          const char *cmd=buildCommand(file_spec.pool(), file_spec, file_spec_cstr, argv);
           char *env_cstr=0;
         if(env) {          if(env) {
                 String string;                  String string(env->pool());
                 Append_env_pair_info info(string);                  env->for_each(append_env_pair, &string);
                 env->for_each(append_env_pair, &info);                  env_cstr=string.cstr(String::UL_UNSPECIFIED);
                 env_cstr=info.string.cstrm(String::L_UNSPECIFIED);  
         }          }
         if(DWORD error=CreateHiddenConsoleProcess(cmd, env_cstr, &pi, &hInWrite, &hOutRead, &hErrRead)) {          if(DWORD error=CreateHiddenConsoleProcess(cmd, env_cstr, &pi, &hInWrite, &hOutRead, &hErrRead)) {
                 char szErrorDesc[MAX_STRING];                  char szErrorDesc[MAX_STRING];
                 const char* param="the file you tried to run";                  char *param="the file you tried to run";
                 size_t error_size=FormatMessage(                  size_t error_size=FormatMessage(
                         FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ARGUMENT_ARRAY , NULL, error,                          FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ARGUMENT_ARRAY , NULL, error,
                         MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),                          MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                         szErrorDesc, sizeof(szErrorDesc), (va_list *)&param);                          szErrorDesc, sizeof(szErrorDesc), &param);
                 if(error_size>3) // ".\r\n"                  if(error_size>3) // ".\r\n"
                         szErrorDesc[error_size-3]=0;                          szErrorDesc[error_size-3]=0;
                           
Line 359  PA_exec_result pa_exec( Line 401  PA_exec_result pa_exec(
                         "exec failed - %s (%ld). Consider adding shbang line (#!x:\\interpreter\\command line)",                           "exec failed - %s (%ld). Consider adding shbang line (#!x:\\interpreter\\command line)", 
                                 error_size?szErrorDesc:"<unknown>", (long)error);                                  error_size?szErrorDesc:"<unknown>", (long)error);
         } else {          } else {
                 const char* in_cstr=in.cstr();                  const char *in_cstr=in.cstr();
                 DWORD written_size;                  DWORD written_size;
                 WriteFile(hInWrite, in_cstr, in.length(), &written_size, NULL);                  WriteFile(hInWrite, in_cstr, in.size(), &written_size, NULL);
                 // EOF for stupid text reads                  // EOF for stupid text reads
                 // normally they should read CONTENT_LENGTH bytes,                  // normally they should read CONTENT_LENGTH bytes,
                 // without this char                  // without this char
                 WriteFile(hInWrite, "\x1A", 1, &written_size, NULL);                  WriteFile(hInWrite, "\x1A", 1, &written_size, NULL);
                 CloseHandle(hInWrite);                  CloseHandle(hInWrite);
                 read_pipe(result.out, hOutRead, String::L_AS_IS);                  read_pipe(out, hOutRead, file_spec_cstr, String::UL_AS_IS, header_pos, eol_marker, eol_marker_size);
                 CloseHandle(hOutRead);                  CloseHandle(hOutRead);
                 read_pipe(result.err, hErrRead, String::L_TAINTED);                               read_pipe(err, hErrRead, file_spec_cstr, String::UL_TAINTED);           
                 CloseHandle(hErrRead);                  CloseHandle(hErrRead);
 /*        /*      
 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  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
Line 384  from http://www.apache.org/websrc/cvsweb Line 426  from http://www.apache.org/websrc/cvsweb
 #else  #else
   
         int pipe_write, pipe_read, pipe_err;          int pipe_write, pipe_read, pipe_err;
           char *file_spec_cstr=file_spec.cstr(String::UL_FILE_SPEC);
   
 #ifdef PA_SAFE_MODE  #ifdef PA_SAFE_MODE
         if(!forced_allow) {          if(!forced_allow) {
                 struct stat finfo;                  struct stat finfo;
                 if(stat(file_spec_cstr, &finfo)!=0)                  if(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);
   
                   if(finfo.st_uid/*foreign?*/!=geteuid()
                           && finfo.st_gid/*foreign?*/!=getegid())
                           throw Exception("parser.runtime", 
                                 &file_spec,                                   &file_spec, 
                                 "stat failed: %s (%d), actual filename '%s'",                                   "parser is in safe mode: executing files of foreign group and user disabled [recompile parser with --disable-safe-mode configure option], actual filename '%s'", 
                                         strerror(errno), errno, file_spec_cstr);                                          file_spec_cstr);
   
                 check_safe_mode(finfo, file_spec, file_spec_cstr);  
         }          }
 #endif  #endif
   
         char* argv_cstrs[1+10+1]={file_spec_cstr, 0};          char *argv_cstrs[1+10+1]={file_spec_cstr, 0};
         const int argv_size=argv.count();          if(argv) {
         const int argv_max=sizeof(argv_cstrs)/sizeof(argv_cstrs[0])-1-1;                  const int argv_size=argv->size();
         if(argv_size>argv_max)                  const int argv_max=sizeof(argv_cstrs)/sizeof(argv_cstrs[0])-1-1;
                 throw Exception("parser.runtime",                  if(argv_size>argv_max)
                         &file_spec,                          throw Exception("parser.runtime",
                         "too many arguments (%d > max %d)", argv_size, argv_max);                                  &file_spec,
         for(int i=0; i<argv_size; i++)                                  "too many arguments (%d > max %d)", argv_size, argv_max);
                 argv_cstrs[1+i]=argv[i]->cstrm();                  for(int i=0; i<argv_size; i++)
         argv_cstrs[1+argv_size]=0;                          argv_cstrs[1+i]=argv->get_string(i)->cstr();
                   argv_cstrs[1+argv_size]=0;
         char **env_cstrs;          }
           char **env_cstrs=0;
         if(env) {          if(env) {
                 env_cstrs=new(PointerFreeGC) char *[env->count()+1/*0*/];                  env_cstrs=
                 Append_env_pair_info info={env_cstrs};                           (char **)env->pool().malloc(sizeof(char *)*(env->size()+1/*0*/));
                 env->for_each(append_env_pair, &info);                  char **env_ref=env_cstrs;
                 *info.env_ref=0;                  env->for_each(append_env_pair, &env_ref);
         } else                  *env_ref=0;
                 env_cstrs=0;          }
   
         pid_t pid=execve_piped(          pid_t pid=execve_piped(
                 file_spec_cstr,                  file_spec_cstr,
Line 424  from http://www.apache.org/websrc/cvsweb Line 473  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.length()) {// there is some in data                  const char *in_cstr=in.cstr();
                         const char* in_cstr=in.cstr();                  if(*in_cstr) // there is some in data
                         write(pipe_write, in_cstr, in.length());                          write(pipe_write, in_cstr, in.size());
                 }  
                 close(pipe_write);                  close(pipe_write);
                 read_pipe(result.out, pipe_read, String::L_AS_IS);                  read_pipe(out, pipe_read, file_spec_cstr, String::UL_AS_IS, header_pos, eol_marker, eol_marker_size);
                 close(pipe_read);                  close(pipe_read);
                 read_pipe(result.err, pipe_err, String::L_TAINTED);                  read_pipe(err, pipe_err, file_spec_cstr, String::UL_TAINTED);
                 close(pipe_err);                  close(pipe_err);
   
                 result.status=get_exit_status(pid); // negative may mean "-errno[execl()]"                  return get_exit_status(pid); // negative may mean "-errno[execl()]"
         } else           } else 
                 throw Exception(0,                  throw Exception(0,
                         &file_spec,                          &file_spec,
                         "%s error: %s (%d)", pid<0?"fork":"pipe", strerror(errno), errno);                           "%s error: %s (%d)", pid<0?"fork":"pipe", strerror(errno), errno); 
 #endif  #endif
   
         return result;          return 0;
 }  }

Removed from v.1.48.2.18.2.16  
changed lines
  Added in v.1.49


E-mail: