1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include <algorithm>
  27 #include "Executor.h"
  28 #include "Log.h"
  29 #include "WinErrorHandling.h"
  30 
  31 
  32 namespace {
  33 
  34 void escapeArg(std::wstring& str) {
  35     if (str.empty()) {
  36         return;
  37     }
  38 
  39     if (str.front() == L'\"' && str.back() == L'\"' && str.size() > 1) {
  40         return;
  41     }
  42 
  43     if (str.find_first_of(L" \t") != std::wstring::npos) {
  44         str = L'"' + str + L'"';
  45     }
  46 }
  47 
  48 } // namespace
  49 
  50 
  51 std::wstring Executor::args() const {
  52     tstring_array tmpArgs;
  53     // argv[0] is the module name.
  54     tmpArgs.push_back(appPath);
  55     tmpArgs.insert(tmpArgs.end(), argsArray.begin(), argsArray.end());
  56 
  57     std::for_each(tmpArgs.begin(), tmpArgs.end(), escapeArg);
  58     return tstrings::join(tmpArgs.begin(), tmpArgs.end(), _T(" "));
  59 }
  60 
  61 
  62 int Executor::execAndWaitForExit() const {
  63     UniqueHandle h = startProcess();
  64 
  65     const DWORD res = ::WaitForSingleObject(h.get(), INFINITE);
  66     if (WAIT_FAILED ==  res) {
  67         JP_THROW(SysError("WaitForSingleObject() failed", WaitForSingleObject));
  68     }
  69 
  70     DWORD exitCode = 0;
  71     if (!GetExitCodeProcess(h.get(), &exitCode)) {
  72         // Error reading process's exit code.
  73         JP_THROW(SysError("GetExitCodeProcess() failed", GetExitCodeProcess));
  74     }
  75 
  76     const DWORD processId = GetProcessId(h.get());
  77     if (!processId) {
  78         JP_THROW(SysError("GetProcessId() failed.", GetProcessId));
  79     }
  80 
  81     LOG_TRACE(tstrings::any() << "Process with PID=" << processId
  82                                 << " terminated. Exit code=" << exitCode);
  83 
  84     return static_cast<int>(exitCode);
  85 }
  86 
  87 
  88 UniqueHandle Executor::startProcess() const {
  89     const std::wstring argsStr = args();
  90 
  91     std::vector<TCHAR> argsBuffer(argsStr.begin(), argsStr.end());
  92     argsBuffer.push_back(0); // terminating '\0'
  93 
  94     STARTUPINFO startupInfo;
  95     ZeroMemory(&startupInfo, sizeof(startupInfo));
  96     startupInfo.cb = sizeof(startupInfo);
  97 
  98     PROCESS_INFORMATION processInfo;
  99     ZeroMemory(&processInfo, sizeof(processInfo));
 100 
 101     DWORD creationFlags = 0;
 102 
 103     if (!theVisible) {
 104         // For GUI applications.
 105         startupInfo.dwFlags |= STARTF_USESHOWWINDOW;
 106         startupInfo.wShowWindow = SW_HIDE;
 107 
 108         // For console applications.
 109         creationFlags |= CREATE_NO_WINDOW;
 110     }
 111 
 112     tstrings::any msg;
 113     msg << "CreateProcess(" << appPath << ", " << argsStr << ")";
 114 
 115     if (!CreateProcess(appPath.c_str(), argsBuffer.data(), NULL, NULL, FALSE,
 116                     creationFlags, NULL, NULL, &startupInfo, &processInfo)) {
 117         msg << " failed";
 118         JP_THROW(SysError(msg, CreateProcess));
 119     }
 120 
 121     msg << " succeeded; PID=" << processInfo.dwProcessId;
 122     LOG_TRACE(msg);
 123 
 124     // Close unneeded handles immediately.
 125     UniqueHandle(processInfo.hThread);
 126 
 127     // Return process handle.
 128     return UniqueHandle(processInfo.hProcess);
 129 }