|
|
| 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 |