|
|
| 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 *)¶m); | szErrorDesc, sizeof(szErrorDesc), ¶m); |
| 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; |
| } | } |