1 /*
   2  * Copyright (c) 2020, 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 <io.h>
  27 #include <fcntl.h>
  28 #include <windows.h>
  29 
  30 #include "AppLauncher.h"
  31 #include "JvmLauncher.h"
  32 #include "Log.h"
  33 #include "Dll.h"
  34 #include "Toolbox.h"
  35 #include "FileUtils.h"
  36 #include "UniqueHandle.h"
  37 #include "ErrorHandling.h"
  38 #include "WinSysInfo.h"
  39 #include "WinErrorHandling.h"
  40 
  41 
  42 // AllowSetForegroundWindow
  43 #pragma comment(lib, "user32")
  44 
  45 
  46 namespace {
  47 
  48 std::unique_ptr<Dll> loadDllWithAlteredPATH(const tstring& dllFullPath) {
  49     LOG_TRACE_FUNCTION();
  50 
  51     const tstring vanillaPathEnvVariable = SysInfo::getEnvVariable(_T("PATH"));
  52 
  53     tstring pathEnvVariable = vanillaPathEnvVariable
  54             + _T(";")
  55             + FileUtils::dirname(dllFullPath);
  56 
  57     SysInfo::setEnvVariable(_T("PATH"), pathEnvVariable);
  58 
  59     LOG_TRACE(tstrings::any() << "New value of PATH: " << pathEnvVariable);
  60 
  61     // Schedule restore of PATH after attempt to load the given dll
  62     const auto resetPATH = runAtEndOfScope([&vanillaPathEnvVariable]() -> void {
  63         SysInfo::setEnvVariable(_T("PATH"), vanillaPathEnvVariable);
  64     });
  65 
  66     return std::unique_ptr<Dll>(new Dll(dllFullPath));
  67 }
  68 
  69 std::unique_ptr<Dll> loadDllWithAddDllDirectory(const tstring& dllFullPath) {
  70     LOG_TRACE_FUNCTION();
  71 
  72     const tstring dirPath = FileUtils::dirname(dllFullPath);
  73 
  74     typedef DLL_DIRECTORY_COOKIE(WINAPI *AddDllDirectoryFunc)(PCWSTR);
  75 
  76     DllFunction<AddDllDirectoryFunc> _AddDllDirectory(
  77             Dll("kernel32.dll", Dll::System()), "AddDllDirectory");
  78 
  79     AddDllDirectoryFunc func = _AddDllDirectory;
  80     DLL_DIRECTORY_COOKIE res = func(dirPath.c_str());
  81     if (!res) {
  82         JP_THROW(SysError(tstrings::any()
  83                 << "AddDllDirectory(" << dirPath << ") failed", func));
  84     }
  85 
  86     LOG_TRACE(tstrings::any() << "AddDllDirectory(" << dirPath << "): OK");
  87 
  88     // Important: use LOAD_LIBRARY_SEARCH_DEFAULT_DIRS flag,
  89     // but not LOAD_LIBRARY_SEARCH_USER_DIRS!
  90     HMODULE dllHandle = LoadLibraryEx(dllFullPath.c_str(), NULL,
  91             LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
  92 
  93     LOG_TRACE(tstrings::any() << "LoadLibraryEx(" << dllFullPath
  94             << ", LOAD_LIBRARY_SEARCH_DEFAULT_DIRS): " << dllHandle);
  95 
  96     const auto freeDll = runAtEndOfScope([&dllHandle]() -> void {
  97         Dll::freeLibrary(dllHandle);
  98     });
  99 
 100     return std::unique_ptr<Dll>(new Dll(dllFullPath));
 101 }
 102 
 103 
 104 class DllWrapper {
 105 public:
 106     DllWrapper(const tstring& dllName) {
 107         try {
 108             // Try load DLL.
 109             dll = std::unique_ptr<Dll>(new Dll(dllName));
 110             LOG_TRACE(tstrings::any() << "Load [" << dllName << "]: OK");
 111         }
 112         catch (const std::exception&) {
 113             // JVM DLL load failed, though it exists in file system.
 114             try {
 115                 // Try adjust the DLL search paths with AddDllDirectory() WINAPI CALL
 116                 dll = loadDllWithAddDllDirectory(dllName);
 117             }
 118             catch (const std::exception&) {
 119                 // AddDllDirectory() didn't work. Try altering PATH environment
 120                 // variable as the last resort.
 121                 dll = loadDllWithAlteredPATH(dllName);
 122             }
 123         }
 124     }
 125 
 126 private:
 127     DllWrapper(const DllWrapper&);
 128     DllWrapper& operator=(const DllWrapper&);
 129 
 130 private:
 131     std::unique_ptr<Dll> dll;
 132 };
 133 
 134 
 135 tstring getJvmLibPath(const Jvm& jvm) {
 136     FileUtils::mkpath path;
 137 
 138     path << FileUtils::dirname(jvm.getPath()) << _T("server") << _T("jvm.dll");
 139 
 140     return path;
 141 }
 142 
 143 
 144 void launchApp() {
 145     // [RT-31061] otherwise UI can be left in back of other windows.
 146     ::AllowSetForegroundWindow(ASFW_ANY);
 147 
 148     const tstring launcherPath = SysInfo::getProcessModulePath();
 149     const tstring appImageRoot = FileUtils::dirname(launcherPath);
 150 
 151     std::unique_ptr<Jvm> jvm(AppLauncher()
 152         .setImageRoot(appImageRoot)
 153         .addJvmLibName(_T("bin\\jli.dll"))
 154         .setAppDir(FileUtils::mkpath() << appImageRoot << _T("app"))
 155         .setDefaultRuntimePath(FileUtils::mkpath() << appImageRoot
 156                 << _T("runtime"))
 157         .createJvmLauncher());
 158 
 159     const DllWrapper jliDll(jvm->getPath());
 160     std::unique_ptr<DllWrapper> splashDll;
 161     if (jvm->isWithSplash()) {
 162         const DllWrapper jvmDll(getJvmLibPath(*jvm));
 163         splashDll = std::unique_ptr<DllWrapper>(new DllWrapper(
 164                 FileUtils::mkpath() 
 165                         << FileUtils::dirname(jvm->getPath()) 
 166                         << _T("splashscreen.dll")));
 167     }
 168 
 169     jvm->launch();
 170 }
 171 
 172 } // namespace
 173 
 174 #ifndef JP_LAUNCHERW
 175 
 176 int __cdecl  wmain() {
 177     return AppLauncher::launch(std::nothrow, launchApp);
 178 }
 179 
 180 #else
 181 
 182 namespace {
 183 
 184 class LastErrorGuiLogAppender : public LogAppender {
 185 public:
 186     virtual void append(const LogEvent& v) {
 187         JP_TRY;
 188 
 189         const std::wstring msg = (tstrings::any()
 190                 << AppLauncher::lastErrorMsg()).wstr();
 191         MessageBox(0, msg.c_str(),
 192             FileUtils::basename(SysInfo::getProcessModulePath()).c_str(),
 193             MB_ICONERROR | MB_OK);
 194 
 195         JP_CATCH_ALL;
 196     }
 197 };
 198 
 199 
 200 class Console {
 201 public:
 202     Console() {
 203         if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
 204             // Failed to connect to parent's console. Create our own.
 205             if (!AllocConsole()) {
 206                 // We already have a console, no need to redirect std I/O.
 207                 return;
 208             }
 209         }
 210 
 211         stdoutChannel = std::unique_ptr<Channel>(new Channel(stdout));
 212         stderrChannel = std::unique_ptr<Channel>(new Channel(stderr));
 213     }
 214 
 215     struct FileCloser {
 216         typedef FILE* pointer;
 217 
 218         void operator()(pointer h) {
 219             ::fclose(h);
 220         }
 221     };
 222 
 223     typedef std::unique_ptr<
 224         FileCloser::pointer,
 225         FileCloser
 226     > UniqueFILEHandle;
 227 
 228 private:
 229     class Channel {
 230     public:
 231         Channel(FILE* stdFILEHandle): stdFILEHandle(stdFILEHandle) {
 232             const char* stdFileName = "CONOUT$";
 233             const char* openMode = "w";
 234             if (stdFILEHandle == stdin) {
 235                 stdFileName = "CONIN$";
 236                 openMode = "r";
 237             }
 238 
 239             FILE* fp = 0;
 240             freopen_s(&fp, stdFileName, openMode, stdFILEHandle);
 241 
 242             fileHandle = UniqueFILEHandle(fp);
 243 
 244             std::ios_base::sync_with_stdio();
 245         }
 246 
 247         virtual ~Channel() {
 248             JP_TRY;
 249 
 250             FILE* fp = 0;
 251             fileHandle = UniqueFILEHandle(fp);
 252             std::ios_base::sync_with_stdio();
 253 
 254             JP_CATCH_ALL;
 255         }
 256 
 257     private:
 258         UniqueFILEHandle fileHandle;
 259         FILE *stdFILEHandle;
 260     };
 261 
 262     std::unique_ptr<Channel> stdoutChannel;
 263     std::unique_ptr<Channel> stderrChannel;
 264 };
 265 
 266 
 267 void launchAppW() {
 268     std::unique_ptr<Console> console;
 269     if (AppLauncher::isWithLogging()) {
 270         console = std::unique_ptr<Console>(new Console());
 271     }
 272 
 273     launchApp();
 274 }
 275 
 276 } // namespace
 277 
 278 
 279 int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) {
 280     LastErrorGuiLogAppender lastErrorLogAppender;
 281     TeeLogAppender logAppender(&AppLauncher::defaultLastErrorLogAppender(),
 282             &lastErrorLogAppender);
 283     return AppLauncher::launch(std::nothrow, launchAppW, &logAppender);
 284 }
 285 
 286 #endif