|
|
| version 1.1.2.1, 2001/04/16 11:26:43 | version 1.28, 2002/03/11 07:53:16 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: program executing for different OS-es. | Parser: program executing for different OS-es. |
| Copyright(c) 2000,2001 ArtLebedev Group(http://www.artlebedev.com) | Copyright(c) 2000,2001, 2002 ArtLebedev Group(http://www.artlebedev.com) |
| Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) | |
| Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf) | |
| $Id$ | $Id$ |
| @todo setrlimit | |
| */ | */ |
| #include "pa_config_includes.h" | #include "pa_config_includes.h" |
| #include "pa_exec.h" | |
| #include "pa_exception.h" | |
| #include "pa_common.h" | |
| #ifdef WIN32 | #ifdef WIN32 |
| # include <windows.h> | # include <windows.h> |
| #else | #else |
| Line 18 | Line 24 |
| # include <sys/wait.h> | # include <sys/wait.h> |
| #endif | #endif |
| #include <stdio.h> | |
| #include <errno.h> | |
| #include "pa_exec.h" | |
| #include "pa_exception.h" | |
| #include "pa_common.h" | |
| #ifdef WIN32 | #ifdef WIN32 |
| /// this func from http://www.ccas.ru/~posp/popov/spawn.htm | /// this func from http://www.ccas.ru/~posp/popov/spawn.htm |
| static BOOL WINAPI CreateHiddenConsoleProcess( LPCTSTR szChildName, | static BOOL WINAPI 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) |
| { | { |
| BOOL fCreated; | BOOL fCreated; |
| STARTUPINFO si; | STARTUPINFO si; |
| Line 45 static BOOL WINAPI CreateHiddenConsolePr | Line 43 static BOOL WINAPI CreateHiddenConsolePr |
| // 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; |
| // Create a child process (suspended) | // Create a child process (suspended) |
| fCreated = CreateProcess( NULL, | fCreated=CreateProcess(NULL, |
| (LPTSTR)szChildName, | (LPTSTR)szCmdLine, |
| NULL, | NULL, |
| NULL, | NULL, |
| TRUE, | TRUE, |
| DETACHED_PROCESS, | 0, //todo CREATE_NO_WINDOW, |
| szEnv, | szEnv, |
| NULL, | NULL, |
| &si, | &si, |
| ppi ); | ppi); |
| CloseHandle( hInRead ); | CloseHandle(hInRead); |
| CloseHandle( hOutWrite ); | CloseHandle(hOutWrite); |
| CloseHandle( hErrWrite ); | CloseHandle(hErrWrite); |
| if( !fCreated ) | if(!fCreated) |
| goto error; | goto error; |
| return TRUE; | return TRUE; |
| error: | error: |
| CloseHandle( *phInWrite ); | CloseHandle(*phInWrite); |
| CloseHandle( *phOutRead ); | CloseHandle(*phOutRead); |
| CloseHandle( *phErrRead ); | CloseHandle(*phErrRead); |
| return FALSE; | return FALSE; |
| } | } |
| static read_pipe(String& result, HANDLE hOutRead, const char *file_spec){ | static void read_pipe(String& result, HANDLE hOutRead, const char *file_spec){ |
| while(true) { | while(true) { |
| char *buf=(char *)result.pool().malloc(MAX_STRING); | char *buf=(char *)result.pool().malloc(MAX_STRING); |
| unsigned long size; | unsigned long size; |
| ReadFile( hOutRead, buf, MAX_STRING, &size, NULL ); | ReadFile(hOutRead, buf, MAX_STRING, &size, NULL); |
| if(!size) | if(!size) |
| break; | break; |
| result.APPEND_CLEAN(buf, size, file_spec, 0); | result.APPEND_AS_IS(buf, size, file_spec, 0); |
| } | } |
| } | } |
| Line 116 static read_pipe(String& result, HANDLE | Line 114 static read_pipe(String& result, HANDLE |
| static const char *buildCommand(Pool& pool, | static const char *buildCommand(Pool& pool, |
| const String& origin_string, | const String& origin_string, |
| const char *file_spec_cstr, const Array *argv) { | const char *file_spec_cstr, const Array *argv) { |
| FILE *f=fopen(file_spec_cstr, "r"); | const char *result=file_spec_cstr; |
| if(f) { | if(FILE *f=fopen(file_spec_cstr, "r")) { |
| 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) { |
| char *atEOL=strchr(buf, '\n'); | const char *begin=buf+2; |
| if(atEOL) { | if(*begin==' ') // alx: were an old magic for some linux-es |
| begin++; | |
| if(char *end=strchr(begin, '\n')) { | |
| String string(pool); | String string(pool); |
| string.APPEND_CLEAN(buf+2, atEOL-(buf+2), | string.APPEND_AS_IS(begin, end-begin, |
| origin_string.origin().file, 0); | origin_string.origin().file, 0); |
| string << " " << file_spec_cstr; | string << " " << file_spec_cstr; |
| if(argv) | if(argv) |
| for(int i=0; i<argv->size(); i++) | for(int i=0; i<argv->size(); i++) |
| string << argv->get_string(i)->cstr(String::UL_AS_IS); | string << argv->get_string(i)->cstr(); |
| file_spec_cstr=string.cstr(); | result=string.cstr(); |
| } | } |
| } | } |
| } | } |
| fclose(f); | fclose(f); |
| } | } |
| return file_spec_cstr; | if(argv) { |
| String buf(pool); | |
| buf << result; | |
| for(int i=0; i<argv->size(); i++) { | |
| buf << " "; | |
| buf << *argv->get_string(i); | |
| } | |
| result=buf.cstr(); | |
| } | |
| return result; | |
| } | } |
| #else | #else |
| static int execle_piped(const char *path, | static int execve_piped(const char *path, |
| const char *arg1, | char * const argv[], char * const env[], |
| const char *arg2, | int *pipe_in, int *pipe_out, int *pipe_err) { |
| const char *arg3, | |
| const char *arg4, | |
| const char *arg5, | |
| char * const env[], | |
| int *pipe_in, int *pipe_out, int *pipe_err) { | |
| int pid; | int pid; |
| int in_fds[2]; | int in_fds[2]; |
| int out_fds[2]; | int out_fds[2]; |
| Line 223 static int execle_piped(const char *path | Line 229 static int execle_piped(const char *path |
| /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */ | /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */ |
| signal(SIGCHLD, SIG_DFL); /* Was that it? */ | signal(SIGCHLD, SIG_DFL); /* Was that it? */ |
| execle(path, arg1, arg2, arg3, arg4, arg5, NULL, env); | execve(path, argv, env); |
| exit(-errno); | exit(-errno); |
| } | } |
| Line 255 static int get_exit_status(int pid) { | Line 261 static int get_exit_status(int pid) { |
| WEXITSTATUS(status) : -2; | WEXITSTATUS(status) : -2; |
| } | } |
| static read_pipe(String& result, int file, const char *file_spec){ | static void read_pipe(String& result, int file, const char *file_spec){ |
| while(true) { | while(true) { |
| char *buf=(char *)result.pool().malloc(MAX_STRING); | char *buf=(char *)result.pool().malloc(MAX_STRING); |
| size_t size=read(file, buf, MAX_STRING); | size_t size=read(file, buf, MAX_STRING); |
| if(!size) | if(!size) |
| break; | break; |
| result.APPEND_CLEAN(buf, size, file_spec, 0); | result.APPEND_AS_IS(buf, size, file_spec, 0); |
| } | } |
| } | } |
| Line 272 static void append_env_pair(const Hash:: | Line 278 static void append_env_pair(const Hash:: |
| String& string=*static_cast<String *>(info); | String& string=*static_cast<String *>(info); |
| string << key << "=" << *static_cast<String *>(value); | string << key << "=" << *static_cast<String *>(value); |
| string.APPEND_CLEAN("", 1, 0, 0); // zero byte | string.APPEND_AS_IS("", 1, 0, 0); // zero byte |
| #else | #else |
| String string(key.pool()); | String string(key.pool()); |
| string << key << "=" << *static_cast<String *>(value); | string << key << "=" << *static_cast<String *>(value); |
| char **env_ptr=static_cast<char **>(info); | char ***env_ref=static_cast<char ***>(info); |
| *env_ptr++=string.cstr(); | **env_ref=string.cstr(); (*env_ref)++; |
| #endif | #endif |
| } | } |
| int pa_exec(const String& file_spec, | int pa_exec(const String& file_spec, |
| Line 287 int pa_exec(const String& file_spec, | Line 293 int pa_exec(const String& file_spec, |
| const String& in, String& out, String& err) { | const String& in, String& out, String& err) { |
| Pool& pool=file_spec.pool(); | Pool& pool=file_spec.pool(); |
| #ifdef NO_PA_EXECS | |
| throw Exception(0, 0, | |
| &file_spec, | |
| "parser execs are disabled [recompile parser without --disable-execs configure option]"); | |
| #ifdef WIN32 | #ifdef WIN32 |
| char pwd[MAX_STRING]; | char pwd[MAX_STRING]; |
| GetCurrentDirectory(sizeof(pwd), pwd); | GetCurrentDirectory(sizeof(pwd), pwd); |
| char *dir=file_spec.cstr(String::UL_FILE_NAME); | char *dir=file_spec.cstr(String::UL_FILE_SPEC); |
| rsplit(dir, '/'); SetCurrentDirectory(dir); | rsplit(dir, '/'); SetCurrentDirectory(dir); |
| PROCESS_INFORMATION pi; | PROCESS_INFORMATION pi; |
| HANDLE hInWrite, hOutRead, hErrRead; | HANDLE hInWrite, hOutRead, hErrRead; |
| char *file_spec_cstr=file_spec.cstr(String::UL_FILE_NAME); | char *file_spec_cstr=file_spec.cstr(String::UL_FILE_SPEC); |
| const char *cmd=buildCommand(file_spec.pool(), file_spec, file_spec_cstr, argv); | const char *cmd=buildCommand(file_spec.pool(), file_spec, file_spec_cstr, argv); |
| char *env_cstr=0; | char *env_cstr=0; |
| if(env) { | if(env) { |
| String string(env->pool()); | String string(env->pool()); |
| env->for_each(append_env_pair, &string); | env->for_each(append_env_pair, &string); |
| env_cstr=string.cstr(String::UL_AS_IS); | env_cstr=string.cstr(); |
| } | } |
| if( CreateHiddenConsoleProcess(cmd, env_cstr, &pi, &hInWrite, &hOutRead, &hErrRead )) { | if(CreateHiddenConsoleProcess(cmd, env_cstr, &pi, &hInWrite, &hOutRead, &hErrRead)) { |
| SetCurrentDirectory(pwd); | SetCurrentDirectory(pwd); |
| const char *in_cstr=in.cstr(); | |
| DWORD written_size; | |
| WriteFile(hInWrite, in_cstr, in.size(), &written_size, NULL); | |
| // EOF for stupid text reads | |
| // normally they should read CONTENT_LENGTH bytes, | |
| // without this char | |
| WriteFile(hInWrite, "\x1A", 1, &written_size, NULL); | |
| CloseHandle(hInWrite); | |
| read_pipe(out, hOutRead, file_spec_cstr); | read_pipe(out, hOutRead, file_spec_cstr); |
| read_pipe(err, hErrRead, file_spec_cstr); | CloseHandle(hOutRead); |
| CloseHandle( hInWrite ); | read_pipe(err, hErrRead, file_spec_cstr); |
| CloseHandle( hOutRead ); | 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 332 from http://www.apache.org/websrc/cvsweb | Line 351 from http://www.apache.org/websrc/cvsweb |
| if(error_size>3) // ".\r\n" | if(error_size>3) // ".\r\n" |
| szErrorDesc[error_size-3]=0; | szErrorDesc[error_size-3]=0; |
| PTHROW(0, 0, | throw Exception(0, 0, |
| &file_spec, | &file_spec, |
| "exec failed - %s (%d)", | "(real command line=\"%s\") exec failed - %s (%ld)", |
| szErrorDesc, | cmd, |
| (long)error); | szErrorDesc, (long)error); |
| } | } |
| #else | #else |
| int pipe_write, pipe_read, pipe_err; | int pipe_write, pipe_read, pipe_err; |
| const char *argv_cstr[5]={"", "", "", "", ""}; | char *file_spec_cstr=file_spec.cstr(String::UL_FILE_SPEC); |
| char *argv_cstrs[1+10+1]={file_spec_cstr, 0}; | |
| if(argv) { | if(argv) { |
| int size=min(5, argv->size()); | const int argv_size=argv->size(); |
| for(int i=0; i<size; i++) | const int argv_max=sizeof(argv_cstrs)/sizeof(argv_cstrs[0])-1-1; |
| argv_cstr[i]=argv->get_string(i)->cstr(String::UL_AS_IS); | if(argv_size>argv_max) |
| throw Exception(0, 0, | |
| &file_spec, | |
| "too many arguments (%d > max %d)", argv_size, argv_max); | |
| for(int i=0; i<argv_size; i++) | |
| argv_cstrs[1+i]=argv->get_string(i)->cstr(); | |
| argv_cstrs[1+argv_size]=0; | |
| } | } |
| const char *file_spec_cstr=file_spec.cstr(String::UL_FILE_NAME); | char **env_cstrs=0; |
| char **env_cstr=0; | |
| if(env) { | if(env) { |
| env_cstr= | env_cstrs= |
| (char **)env->pool().malloc(sizeof(char *)*(env->size()+1/*0*/)); | (char **)env->pool().malloc(sizeof(char *)*(env->size()+1/*0*/)); |
| char **env_ptr=env_cstr; | char **env_ref=env_cstrs; |
| env->for_each(append_env_pair, &env_ptr); | env->for_each(append_env_pair, &env_ref); |
| *env_ptr=0; | *env_ref=0; |
| } | } |
| if(int pid=execle_piped( | const char *pwd=getcwd(NULL, 0); |
| char dir[MAX_STRING]; | |
| strncpy(dir, file_spec_cstr, MAX_STRING); | |
| rsplit(dir,'/'); // trim filename | |
| chdir(dir); | |
| int pid=execve_piped( | |
| file_spec_cstr, | file_spec_cstr, |
| argv_cstr[0], argv_cstr[1], argv_cstr[2], argv_cstr[3], argv_cstr[4], | argv_cstrs, env_cstrs, |
| env_cstr, | &pipe_write, &pipe_read, &pipe_err); |
| &pipe_write, &pipe_read, &pipe_err)) { | if (pwd) |
| chdir(pwd); | |
| const char *in_cstr=in.cstr(String::UL_AS_IS); | if(pid) { |
| // in child | |
| const char *in_cstr=in.cstr(); | |
| write(pipe_write, in_cstr, in.size()); | write(pipe_write, in_cstr, in.size()); |
| close(pipe_write); | close(pipe_write); |
| read_pipe(out, pipe_read, file_spec_cstr); | read_pipe(out, pipe_read, file_spec_cstr); |
| read_pipe(err, pipe_err, file_spec_cstr); | |
| close(pipe_read); | close(pipe_read); |
| read_pipe(err, pipe_err, file_spec_cstr); | |
| close(pipe_err); | close(pipe_err); |
| return get_exit_status(pid); // negative may mean "-errno[execl()]" | return get_exit_status(pid); // negative may mean "-errno[execl()]" |
| } else | } else |
| PTHROW(0, 0, | throw Exception(0, 0, |
| &file_spec, | &file_spec, |
| "pipe error"); | "pipe error"); |
| #endif | #endif |
| #endif | |
| return 0; | return 0; |
| } | } |