|
|
| version 1.1.2.2, 2001/04/16 14:46:55 | 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,2001 ArtLebedev Group(http://www.artlebedev.com) | Copyright (c) 2001-2024 Art. Lebedev Studio (http://www.artlebedev.com) |
| Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru> | |
| Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf) | @todo setrlimit |
| $Id$ | |
| */ | */ |
| #include "pa_config_includes.h" | #include "pa_config_includes.h" |
| #ifdef WIN32 | |
| # include <windows.h> | |
| #else | |
| # include <signal.h> | |
| # include <sys/types.h> | |
| # include <sys/wait.h> | |
| #endif | |
| #include <stdio.h> | |
| #include <errno.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" |
| volatile const char * IDENT_PA_EXEC_C="$Id$" IDENT_PA_EXEC_H; | |
| #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 BOOL WINAPI CreateHiddenConsoleProcess( LPCTSTR szChildName, | static DWORD CreateHiddenConsoleProcess(LPCTSTR szCmdLine, |
| char *szEnv, | LPCTSTR szScriptFileSpec, |
| PROCESS_INFORMATION* ppi, | char *szEnv, |
| LPHANDLE phInWrite, | PROCESS_INFORMATION* ppi, |
| LPHANDLE phOutRead, | LPHANDLE phInWrite, |
| LPHANDLE phErrRead ) | LPHANDLE phOutRead, |
| LPHANDLE phErrRead) | |
| { | { |
| BOOL fCreated; | DWORD result=0; |
| STARTUPINFO si; | BOOL fCreated; |
| SECURITY_ATTRIBUTES sa={0}; | STARTUPINFO si; |
| HANDLE hInRead; | SECURITY_ATTRIBUTES sa={0}; |
| HANDLE hOutWrite; | HANDLE hInRead; |
| HANDLE hErrWrite; | HANDLE hOutWrite; |
| HANDLE hErrWrite; | |
| // Create pipes | |
| // initialize security attributes for handle inheritance (for WinNT) | // Create pipes |
| sa.nLength = sizeof( sa ); | // initialize security attributes for handle inheritance (for WinNT) |
| sa.bInheritHandle = TRUE; | sa.nLength=sizeof(sa); |
| sa.lpSecurityDescriptor = NULL; | sa.bInheritHandle=TRUE; |
| sa.lpSecurityDescriptor=NULL; | |
| // create STDIN pipe | |
| if( !CreatePipe( &hInRead, phInWrite, &sa, 0 )) | // create STDIN pipe |
| goto error; | if(!CreatePipe(&hInRead, phInWrite, &sa, 0)) |
| goto error; | |
| // create STDOUT pipe | |
| if( !CreatePipe( phOutRead, &hOutWrite, &sa, 0 )) | // Ensure the write handle to the pipe for STDIN is not inherited. |
| goto error; | if (!SetHandleInformation(*phInWrite, HANDLE_FLAG_INHERIT, 0)) |
| goto error; | |
| // create STDERR pipe | |
| if( !CreatePipe( phErrRead, &hErrWrite, &sa, 0 )) | // create STDOUT pipe |
| goto error; | if(!CreatePipe(phOutRead, &hOutWrite, &sa, 0)) |
| goto error; | |
| // process startup information | |
| memset( &si, 0, sizeof( si )); | // Ensure the read handle to the pipe for STDOUT is not inherited. |
| si.cb = sizeof( si ); | if (!SetHandleInformation(*phOutRead, HANDLE_FLAG_INHERIT, 0)) |
| si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; | goto error; |
| // child process' console must be hidden for Win95 compatibility | |
| si.wShowWindow = SW_HIDE; | // create STDERR pipe |
| // assign "other" sides of pipes | if(!CreatePipe(phErrRead, &hErrWrite, &sa, 0)) |
| // si.hStdInput = hInRead; | goto error; |
| si.hStdOutput = hOutWrite; | |
| si.hStdError = hErrWrite; | // Ensure the read handle to the pipe for STDERR is not inherited. |
| if (!SetHandleInformation(*phErrRead, HANDLE_FLAG_INHERIT, 0)) | |
| // Create a child process (suspended) | goto error; |
| fCreated = CreateProcess( NULL, | |
| (LPTSTR)szChildName, | // process startup information |
| NULL, | memset(&si, 0, sizeof(si)); |
| NULL, | si.cb=sizeof(si); |
| TRUE, | si.dwFlags=STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; |
| DETACHED_PROCESS, | // child process' console must be hidden for Win95 compatibility |
| szEnv, | si.wShowWindow=SW_HIDE; |
| NULL, | // assign "other" sides of pipes |
| &si, | si.hStdInput=hInRead; |
| ppi ); | si.hStdOutput=hOutWrite; |
| si.hStdError=hErrWrite; | |
| CloseHandle( hInRead ); | |
| CloseHandle( hOutWrite ); | // calculating script's directory |
| CloseHandle( hErrWrite ); | char dir[MAX_STRING]; |
| pa_strncpy(dir, szScriptFileSpec, MAX_STRING); | |
| lsplit(dir,' '); // trim arguments | |
| rsplit(dir,'/'); rsplit(dir,'\\'); // trim filename | |
| // Create a child process (suspended) | |
| fCreated=CreateProcess(NULL, | |
| (LPTSTR)szCmdLine, | |
| NULL, | |
| NULL, | |
| TRUE, | |
| CREATE_NO_WINDOW, | |
| szEnv, | |
| dir, | |
| &si, | |
| ppi); | |
| if(!fCreated) | |
| result=GetLastError(); | |
| CloseHandle(hInRead); | |
| CloseHandle(hOutWrite); | |
| CloseHandle(hErrWrite); | |
| if(!fCreated) | |
| goto error; | |
| return result; | |
| error: | |
| if(!result/*yet*/) | |
| result=GetLastError(); // get it | |
| CloseHandle(*phInWrite); | |
| CloseHandle(*phOutRead); | |
| CloseHandle(*phErrRead); | |
| return result; | |
| } | |
| if( !fCreated ) | static int get_exit_status(HANDLE hProcess) { |
| goto error; | 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; | |
| } | |
| return TRUE; | static void read_pipe(String& result, HANDLE hOutRead, String::Language lang){ |
| while(true) { | |
| char *buf=new(PointerFreeGC) char[MAX_STRING+1]; | |
| DWORD size=0; | |
| if(!ReadFile(hOutRead, buf, MAX_STRING, &size, NULL) || !size) | |
| break; | |
| buf[size]=0; | |
| result.append_know_length(buf, size, lang); | |
| } | |
| } | |
| error: | static void read_pipe(File_read_result& result, HANDLE hOutRead){ |
| CloseHandle( *phInWrite ); | |
| CloseHandle( *phOutRead ); | |
| CloseHandle( *phErrRead ); | |
| return FALSE; | char *buf=(char*)pa_malloc(MAX_STRING+1); |
| } | DWORD bufsize = MAX_STRING; |
| result.headers = 0; | |
| result.length = 0; | |
| result.str = 0; | |
| result.success = false; | |
| static void read_pipe(String& result, HANDLE hOutRead, const char *file_spec){ | |
| while(true) { | while(true) { |
| char *buf=(char *)result.pool().malloc(MAX_STRING); | DWORD size=0; |
| unsigned long size; | if(!ReadFile(hOutRead, buf + result.length, bufsize - result.length, &size, NULL) || !size) |
| ReadFile( hOutRead, buf, MAX_STRING, &size, NULL ); | |
| if(!size) | |
| break; | break; |
| result.APPEND_CLEAN(buf, size, file_spec, 0); | result.length += size; |
| } | if(result.length >= bufsize){ |
| bufsize *= 2; | |
| buf=(char*)pa_realloc(buf, bufsize+1); | |
| } | |
| result.str=buf; | |
| } | |
| } | } |
| 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; | |
| } | |
| static const char *buildCommand(Pool& pool, | *dest++ = '"'; |
| const String& origin_string, | *dest = '\0'; |
| const char *file_spec_cstr, const Array *argv) { | return result; |
| FILE *f=fopen(file_spec_cstr, "r"); | } |
| if(f) { | |
| static const char* buildCommand(const char* file_spec_cstr, const ArrayString& argv) { | |
| const char* result=file_spec_cstr; | |
| if(FILE *f=pa_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) { |
| char *atEOL=strchr(buf, '\n'); | const char* begin=buf+2; |
| if(atEOL) { | while(*begin==' ') // alx: were an old magic for some linux-es |
| String string(pool); | begin++; |
| string.APPEND_CLEAN(buf+2, atEOL-(buf+2), | if(const char *end=strchr(begin, '\n')) { |
| origin_string.origin().file, 0); | String string(pa_strdup(begin, end-begin)); |
| string << " " << file_spec_cstr; | string << " " << file_spec_cstr; |
| if(argv) | result=string.cstr(); |
| for(int i=0; i<argv->size(); i++) | |
| string << argv->get_string(i)->cstr(String::UL_AS_IS); | |
| file_spec_cstr=string.cstr(); | |
| } | } |
| } | } |
| } | } |
| } catch(...) { | |
| fclose(f); | |
| rethrow; | |
| } | |
| fclose(f); | fclose(f); |
| } | } |
| return file_spec_cstr; | |
| { // appending argv | |
| String string(result); | |
| for(size_t i=0; i<argv.count(); i++) { | |
| string << " "; | |
| string << arg_quote(argv[i]->cstr()); | |
| } | |
| result=string.cstr(); | |
| } | |
| return result; | |
| } | } |
| #else | #else |
| static int execle_piped(const char *path, | static pid_t execve_piped(const char* file_spec_cstr, |
| const char *arg1, | char * const argv[], char * const env[], |
| const char *arg2, | int *pipe_in, int *pipe_out, int *pipe_err) { |
| const char *arg3, | pid_t pid; |
| const char *arg4, | |
| const char *arg5, | |
| char * const env[], | |
| int *pipe_in, int *pipe_out, int *pipe_err) { | |
| int pid; | |
| int in_fds[2]; | int in_fds[2]; |
| int out_fds[2]; | int out_fds[2]; |
| int err_fds[2]; | int err_fds[2]; |
| Line 196 static int execle_piped(const char *path | Line 284 static int execle_piped(const char *path |
| close(err_fds[0]); close(err_fds[1]); | close(err_fds[0]); close(err_fds[1]); |
| } | } |
| errno=save_errno; | errno=save_errno; |
| return 0; | return -1; |
| } | } |
| if(!pid) { | if(!pid) { |
| Line 220 static int execle_piped(const char *path | Line 308 static int execle_piped(const char *path |
| close(err_fds[1]); | close(err_fds[1]); |
| } | } |
| /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */ | /* grabbed this from Apache source: */ |
| /* 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); | // chdir to script's directory |
| char dir[MAX_STRING]; | |
| pa_strncpy(dir, file_spec_cstr, MAX_STRING); | |
| rsplit(dir,'/'); // trim filename | |
| chdir(dir); | |
| // execute | |
| execve(file_spec_cstr, argv, env); | |
| exit(-errno); | exit(-errno); |
| } | } |
| Line 248 static int execle_piped(const char *path | Line 344 static int execle_piped(const char *path |
| } | } |
| static int get_exit_status(int pid) { | static int get_exit_status(int pid) { |
| int status; | int status=0; |
| if(!waitpid(pid, &status, 0)) | pid_t cid; |
| while ((cid=waitpid(pid, &status, WUNTRACED)) == -1 && errno == EINTR); | |
| if(!cid) | |
| return -1; | return -1; |
| return WIFEXITED(status) ? | return WIFEXITED(status) ? |
| WEXITSTATUS(status) : -2; | WEXITSTATUS(status) : -2; |
| } | } |
| static void read_pipe(String& result, int file, const char *file_spec){ | static void read_pipe(String& result, int file, String::Language lang){ |
| while(true) { | while(true) { |
| char *buf=(char *)result.pool().malloc(MAX_STRING); | char *buf=new(PointerFreeGC) char[MAX_STRING+1]; |
| size_t size=read(file, buf, MAX_STRING); | ssize_t length=read(file, buf, MAX_STRING); |
| if(!size) | if(length<=0) |
| break; | break; |
| result.APPEND_CLEAN(buf, size, file_spec, 0); | buf[length]=0; |
| } | result.append_know_length(buf, length, lang); |
| } | |
| } | |
| static void read_pipe(File_read_result& result, int file){ | |
| char *buf=(char*)pa_malloc(MAX_STRING+1); | |
| size_t bufsize = MAX_STRING; | |
| result.headers = 0; | |
| result.length = 0; | |
| result.str = 0; | |
| result.success = false; | |
| while(true) { | |
| ssize_t size=read(file, buf + result.length, bufsize - result.length); | |
| if(size <= 0) | |
| break; | |
| result.length += size; | |
| if(result.length >= bufsize){ | |
| bufsize *= 2; | |
| buf=(char*)pa_realloc(buf, bufsize+1); | |
| } | |
| result.str=buf; | |
| } | |
| } | } |
| #endif | #endif |
| static void append_env_pair(const Hash::Key& key, Hash::Val *value, void *info) { | #ifndef DOXYGEN |
| #ifdef WIN32 | struct Append_env_pair_info { |
| String& string=*static_cast<String *>(info); | #ifdef _MSC_VER |
| String::Body& body; | |
| string << key << "=" << *static_cast<String *>(value); | Append_env_pair_info(String::Body& abody): body(abody) {} |
| string.APPEND_CLEAN("", 1, 0, 0); // zero byte | #else |
| char **env_ref; | |
| #endif | |
| }; | |
| #endif | |
| static void append_env_pair(HashStringString::key_type key, HashStringString::value_type value, | |
| Append_env_pair_info *info) { | |
| #ifdef _MSC_VER | |
| info->body << key << "=" << value; | |
| info->body.append_know_length("\1", 1); // placeholder for of zero byte | |
| #else | #else |
| String string(key.pool()); | String::Body body; |
| string << key << "=" << *static_cast<String *>(value); | body << key << "=" << value.cstr(); |
| char **env_ptr=static_cast<char **>(info); | *(info->env_ref++)=body.cstrm(); |
| *env_ptr++=string.cstr(); | |
| #endif | #endif |
| } | } |
| int pa_exec(const String& file_spec, | |
| const Hash *env, | PA_exec_result pa_exec(bool forced_allow, const String& file_spec, const HashStringString* env, const ArrayString& argv, String::C in) { |
| const Array *argv, | PA_exec_result result; |
| const String& in, String& out, String& err) { | |
| Pool& pool=file_spec.pool(); | #ifdef NO_PA_EXECS |
| if(!forced_allow) | |
| #ifdef WIN32 | throw Exception(PARSER_RUNTIME, &file_spec, "parser execs are disabled [recompile parser without --disable-execs configure option]"); |
| #endif | |
| char pwd[MAX_STRING]; | |
| GetCurrentDirectory(sizeof(pwd), pwd); | #ifdef _MSC_VER |
| char *dir=file_spec.cstr(String::UL_FILE_NAME); | |
| 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); | const char* script_spec_cstr=file_spec.taint_cstr(String::L_FILE_SPEC); |
| const char *cmd=buildCommand(file_spec.pool(), file_spec, file_spec_cstr, argv); | const char* cmd=buildCommand(script_spec_cstr, argv); |
| char *env_cstr=0; | char* env_cstr=0; |
| if(env) { | if(env) { |
| String string(env->pool()); | String::Body body; |
| env->for_each(append_env_pair, &string); | Append_env_pair_info info(body); |
| env_cstr=string.cstr(String::UL_AS_IS); | env->for_each<Append_env_pair_info*>(append_env_pair, &info); |
| } | env_cstr=info.body.cstrm(); |
| if( CreateHiddenConsoleProcess(cmd, env_cstr, &pi, &hInWrite, &hOutRead, &hErrRead )) { | for(char* replacer=env_cstr; *replacer; replacer++) |
| SetCurrentDirectory(pwd); | if(*replacer=='\1') |
| *replacer=0; | |
| read_pipe(out, hOutRead, file_spec_cstr); | } |
| read_pipe(err, hErrRead, file_spec_cstr); | if(DWORD error=CreateHiddenConsoleProcess(cmd, script_spec_cstr, env_cstr, &pi, &hInWrite, &hOutRead, &hErrRead)) { |
| CloseHandle( hInWrite ); | |
| CloseHandle( hOutRead ); | |
| 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 | |
| * We must close the handles to the new process and its main thread | |
| * to prevent handle and memory leaks. | |
| */ | |
| CloseHandle(pi.hProcess); | |
| CloseHandle(pi.hThread); | |
| } else { | |
| SetCurrentDirectory(pwd); | |
| DWORD error=GetLastError(); | |
| char szErrorDesc[MAX_STRING]; | char szErrorDesc[MAX_STRING]; |
| FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, | const char* param="the file you tried to run"; |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), | size_t error_size=FormatMessage( |
| szErrorDesc, sizeof(szErrorDesc), NULL); | FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ARGUMENT_ARRAY , NULL, error, |
| size_t error_size=strlen(szErrorDesc); | MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), |
| szErrorDesc, sizeof(szErrorDesc), (va_list *)¶m); | |
| 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("file.execute", &file_spec, "exec failed - %s (%u). Consider adding shbang line (#!x:\\interpreter\\command line)", error_size ? szErrorDesc : "<unknown>", error); |
| &file_spec, | } else { |
| "exec failed - %s (%d)", | DWORD written_size; |
| szErrorDesc, | if(in.length>0) |
| (long)error); | WriteFile(hInWrite, in.str, in.length, &written_size, NULL); |
| CloseHandle(hInWrite); | |
| read_pipe(result.out, hOutRead); | |
| CloseHandle(hOutRead); | |
| read_pipe(result.err, hErrRead, String::L_TAINTED); | |
| CloseHandle(hErrRead); | |
| result.status=get_exit_status(pi.hProcess); | |
| // We must close the handles to the new process and its main thread | |
| // to prevent handle and memory leaks. | |
| CloseHandle(pi.hProcess); | |
| CloseHandle(pi.hThread); | |
| } | } |
| #else | #else |
| // execve needs non const | |
| char* file_spec_cstr=file_spec.taint_cstrm(String::L_FILE_SPEC); | |
| int pipe_write, pipe_read, pipe_err; | int pipe_write, pipe_read, pipe_err; |
| const char *argv_cstr[5]={"", "", "", "", ""}; | |
| if(argv) { | if(!forced_allow) { |
| int size=min(5, argv->size()); | struct stat finfo; |
| for(int i=0; i<size; i++) | if(pa_stat(file_spec_cstr, &finfo)!=0) |
| argv_cstr[i]=argv->get_string(i)->cstr(String::UL_AS_IS); | throw Exception("file.missing", &file_spec, "stat failed: %s (%d), actual filename '%s'", strerror(errno), errno, file_spec_cstr); |
| } | |
| const char *file_spec_cstr=file_spec.cstr(String::UL_FILE_NAME); | check_safe_mode(finfo, file_spec, file_spec_cstr); |
| char **env_cstr=0; | } |
| char* argv_cstrs[1+100+1]={file_spec_cstr, 0}; | |
| const int argv_size=argv.count(); | |
| const int argv_max=sizeof(argv_cstrs)/sizeof(argv_cstrs[0])-1-1; | |
| if(argv_size>argv_max) | |
| throw Exception(PARSER_RUNTIME, &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[i]->cstrm(); | |
| argv_cstrs[1+argv_size]=0; | |
| char **env_cstrs; | |
| if(env) { | if(env) { |
| env_cstr= | env_cstrs=new(PointerFreeGC) char *[env->count()+1/*0*/]; |
| (char **)env->pool().malloc(sizeof(char *)*(env->size()+1/*0*/)); | Append_env_pair_info info={env_cstrs}; |
| char **env_ptr=env_cstr; | env->for_each(append_env_pair, &info); |
| env->for_each(append_env_pair, &env_ptr); | *info.env_ref=0; |
| *env_ptr=0; | } else |
| } | env_cstrs=0; |
| if(int pid=execle_piped( | |
| file_spec_cstr, | |
| argv_cstr[0], argv_cstr[1], argv_cstr[2], argv_cstr[3], argv_cstr[4], | |
| env_cstr, | |
| &pipe_write, &pipe_read, &pipe_err)) { | |
| const char *in_cstr=in.cstr(String::UL_AS_IS); | pid_t pid=execve_piped( |
| write(pipe_write, in_cstr, in.size()); | file_spec_cstr, |
| argv_cstrs, env_cstrs, | |
| &pipe_write, &pipe_read, &pipe_err); | |
| if(pid>0) { | |
| // in child | |
| if(in.length>0) // there is some in data | |
| write(pipe_write, in.str, in.length); | |
| close(pipe_write); | close(pipe_write); |
| read_pipe(out, pipe_read, file_spec_cstr); | read_pipe(result.out, pipe_read); |
| read_pipe(err, pipe_err, file_spec_cstr); | |
| close(pipe_read); | close(pipe_read); |
| read_pipe(result.err, pipe_err, String::L_TAINTED); | |
| close(pipe_err); | close(pipe_err); |
| return get_exit_status(pid); // negative may mean "-errno[execl()]" | result.status=get_exit_status(pid); // negative may mean "-errno[execl()]" |
| } else | } else { |
| PTHROW(0, 0, | const char* str=strerror(errno); |
| &file_spec, | throw Exception("file.execute", &file_spec, "%s error: %s (%d)", pid<0?"fork":"pipe", str?str:"<unknown>", errno); |
| "pipe error"); | } |
| #endif | #endif |
| return 0; | return result; |
| } | } |