| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include "ProcessUtils.h" |
| #include "StringUtils.h" |
| #include "FileUtils.h" |
| |
| |
| const DWORD DEFAULT_PROCESS_TIMEOUT = 30000; //30 sec |
| |
| DWORD readBuf(HANDLE hRead, WCHAR * buf, DWORD * bytesRead, HANDLE hWrite) { |
| ReadFile(hRead, buf, STREAM_BUF_LENGTH - 1, bytesRead, NULL); |
| |
| if((*bytesRead)>0 && hWrite!=INVALID_HANDLE_VALUE) { |
| WriteFile(hWrite, buf, (*bytesRead), NULL, 0); |
| } |
| ZERO(buf, STREAM_BUF_LENGTH); |
| return 0; |
| } |
| |
| DWORD readNextData(HANDLE hRead, WCHAR * buf, HANDLE hWrite) { |
| DWORD bytesRead; |
| DWORD bytesAvailable; |
| ZERO(buf, STREAM_BUF_LENGTH); |
| |
| PeekNamedPipe(hRead, buf, STREAM_BUF_LENGTH - 1, &bytesRead, &bytesAvailable, NULL); |
| if (bytesRead != 0) { |
| ZERO(buf, STREAM_BUF_LENGTH); |
| if (bytesAvailable >= STREAM_BUF_LENGTH) { |
| while (bytesRead >= STREAM_BUF_LENGTH-1) { |
| readBuf(hRead, buf, &bytesRead, hWrite); |
| } |
| } |
| else { |
| readBuf(hRead, buf, &bytesRead, hWrite); |
| } |
| return bytesRead; |
| } |
| return 0; |
| } |
| |
| // get already running process stdout |
| DWORD readProcessStream(PROCESS_INFORMATION pi, HANDLE currentProcessStdin, HANDLE currentProcessStdout, HANDLE currentProcessStderr, DWORD timeOut, HANDLE hWriteInput, HANDLE hWriteOutput, HANDLE hWriteError) { |
| DWORD started = GetTickCount(); |
| WCHAR buf[STREAM_BUF_LENGTH]; |
| DWORD exitCode=0; |
| DWORD outRead =0; |
| DWORD errRead =0; |
| DWORD inRead =0; |
| while(1) { |
| outRead = readNextData(currentProcessStdout, buf, hWriteOutput); |
| errRead = readNextData(currentProcessStderr, buf, hWriteError); |
| inRead = readNextData(hWriteInput, buf, currentProcessStdin); |
| GetExitCodeProcess(pi.hProcess, &exitCode); |
| if (exitCode != STILL_ACTIVE) break; |
| |
| if(outRead == 0 && errRead==0 && inRead==0 && timeOut!=INFINITE) { |
| if((GetTickCount() - started) > timeOut) break; |
| } |
| //avoid extra using of CPU resources |
| Sleep(1); |
| } |
| return exitCode; |
| } |
| char * readHandle(HANDLE hRead) { |
| char * output = NULL; |
| char * buf = newpChar(STREAM_BUF_LENGTH); |
| DWORD total = 0; |
| DWORD read; |
| DWORD bytesRead; |
| DWORD bytesAvailable; |
| |
| while(1) { |
| PeekNamedPipe(hRead, buf, STREAM_BUF_LENGTH - 1, &bytesRead, &bytesAvailable, NULL); |
| if(bytesAvailable==0) break; |
| ReadFile(hRead, buf, STREAM_BUF_LENGTH - 1, &read, NULL); |
| if(read==0) break; |
| output = appendStringN(output, total, buf, read); |
| total+=read; |
| } |
| FREE(buf); |
| return output; |
| } |
| |
| |
| |
| |
| // run process and get its standart output |
| // command - executing command |
| // timeLimitMillis - timeout of the process running without any output |
| // dir - working directory |
| // return ERROR_ON_EXECUTE_PROCESS for serios error |
| // return ERROR_PROCESS_TIMEOUT for timeout |
| |
| void executeCommand(LauncherProperties * props, WCHAR * command, WCHAR * dir, DWORD timeLimitMillis, HANDLE hWriteOutput, HANDLE hWriteError, DWORD priority) { |
| STARTUPINFOW si; |
| SECURITY_ATTRIBUTES sa; |
| SECURITY_DESCRIPTOR sd; |
| PROCESS_INFORMATION pi; |
| |
| HANDLE newProcessInput; |
| HANDLE newProcessOutput; |
| HANDLE newProcessError; |
| |
| HANDLE currentProcessStdout; |
| HANDLE currentProcessStdin; |
| HANDLE currentProcessStderr; |
| |
| WCHAR * directory; |
| |
| InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); |
| SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); |
| sa.lpSecurityDescriptor = &sd; |
| sa.nLength = sizeof(SECURITY_ATTRIBUTES); |
| sa.bInheritHandle = TRUE; |
| |
| |
| if (!CreatePipe(&newProcessInput, ¤tProcessStdin, &sa, 0)) { |
| writeErrorA(props, OUTPUT_LEVEL_NORMAL, 1, "Can`t create pipe for input. ", NULL , GetLastError()); |
| props->status = ERROR_ON_EXECUTE_PROCESS; |
| return; |
| } |
| |
| if (!CreatePipe(¤tProcessStdout, &newProcessOutput, &sa, 0)) { |
| writeErrorA(props, OUTPUT_LEVEL_NORMAL, 1, "Can`t create pipe for output. ", NULL , GetLastError()); |
| CloseHandle(newProcessInput); |
| CloseHandle(currentProcessStdin); |
| props->status = ERROR_ON_EXECUTE_PROCESS; |
| return; |
| } |
| |
| if (!CreatePipe(¤tProcessStderr, &newProcessError, &sa, 0)) { |
| writeErrorA(props, OUTPUT_LEVEL_NORMAL, 1, "Can`t create pipe for error. ", NULL , GetLastError()); |
| CloseHandle(newProcessInput); |
| CloseHandle(currentProcessStdin); |
| CloseHandle(newProcessOutput); |
| CloseHandle(currentProcessStdout); |
| props->status = ERROR_ON_EXECUTE_PROCESS; |
| return; |
| } |
| |
| |
| GetStartupInfoW(&si); |
| |
| si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; |
| si.wShowWindow = SW_HIDE; |
| si.hStdOutput = newProcessOutput; |
| si.hStdError = newProcessError; |
| si.hStdInput = newProcessInput; |
| |
| directory = (dir!=NULL) ? dir : getCurrentDirectory(); |
| writeMessageA(props, OUTPUT_LEVEL_NORMAL, 0, "Create new process: ", 1); |
| writeMessageA(props, OUTPUT_LEVEL_NORMAL, 0, " command : ", 0); |
| writeMessageW(props, OUTPUT_LEVEL_NORMAL, 0, command, 1); |
| writeMessageA(props, OUTPUT_LEVEL_NORMAL, 0, " directory : ", 0); |
| writeMessageW(props, OUTPUT_LEVEL_NORMAL, 0, directory, 1); |
| |
| props->exitCode = ERROR_OK; |
| if (CreateProcessW(NULL, command, NULL, NULL, TRUE, |
| CREATE_NEW_CONSOLE | CREATE_NO_WINDOW | CREATE_DEFAULT_ERROR_MODE | priority, |
| NULL, directory, &si, &pi)) { |
| // TODO |
| // Check whether volder virtualization can brake things and provide method to disable it if necessary |
| // I am not sure whether we need it off or on. |
| // http://www.netbeans.org/issues/show_bug.cgi?id=122186 |
| DWORD timeOut = ((timeLimitMillis<=0) ? DEFAULT_PROCESS_TIMEOUT: timeLimitMillis); |
| props->status = ERROR_OK; |
| writeMessageA(props, OUTPUT_LEVEL_DEBUG, 0, "... process created", 1); |
| |
| props->exitCode = readProcessStream(pi, currentProcessStdin, currentProcessStdout, currentProcessStderr, timeOut, newProcessInput, hWriteOutput, hWriteError); |
| |
| if(props->exitCode==STILL_ACTIVE) { |
| //actually we have reached the timeout of the process and need to terminate it |
| writeMessageA(props, OUTPUT_LEVEL_DEBUG, 1, "... process is timeouted", 1); |
| GetExitCodeProcess(pi.hProcess, & (props->exitCode)); |
| |
| if(props->exitCode==STILL_ACTIVE) { |
| TerminateProcess(pi.hProcess, 0); |
| writeMessageA(props, OUTPUT_LEVEL_DEBUG, 1, "... terminate process", 1); |
| //Terminating process...It worked too much without any stdout/stdin/stderr |
| props->status = ERROR_PROCESS_TIMEOUT;//terminated by timeout |
| } |
| } else { |
| //application finished its work... succesfully or not - it doesn`t matter |
| writeMessageA(props, OUTPUT_LEVEL_DEBUG, 0, "... process finished his work", 1); |
| } |
| CloseHandle(pi.hThread); |
| CloseHandle(pi.hProcess); |
| } else { |
| writeErrorA(props, OUTPUT_LEVEL_DEBUG, 1, "... can`t create process.", NULL, GetLastError()); |
| props->status = ERROR_ON_EXECUTE_PROCESS; |
| } |
| |
| |
| CloseHandle(newProcessInput); |
| CloseHandle(newProcessOutput); |
| CloseHandle(newProcessError); |
| CloseHandle(currentProcessStdin); |
| CloseHandle(currentProcessStdout); |
| CloseHandle(currentProcessStderr); |
| } |
| |
| |
| |