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, bool isClientJvm) {
 136     FileUtils::mkpath path;
 137     path << FileUtils::dirname(jvm.getPath());
 138     if (isClientJvm) {
 139         path << _T("client");
 140     } else {
 141         path << _T("server");
 142     }
 143     path << _T("jvm.dll");
 144 
 145     if (isClientJvm && !FileUtils::isFileExists(path)) {
 146         return getJvmLibPath(jvm, false);
 147     }
 148 
 149     return path;
 150 }
 151 
 152 
 153 void launchApp() {
 154     // [RT-31061] otherwise UI can be left in back of other windows.
 155     ::AllowSetForegroundWindow(ASFW_ANY);
 156 
 157     const tstring launcherPath = SysInfo::getProcessModulePath();
 158     const tstring appImageRoot = FileUtils::dirname(launcherPath);
 159 
 160     std::unique_ptr<Jvm> jvm(AppLauncher()
 161         .setImageRoot(appImageRoot)
 162         .addJvmLibName(_T("bin\\jli.dll"))
 163         .setAppDir(FileUtils::mkpath() << appImageRoot << _T("app"))
 164         .setDefaultRuntimePath(FileUtils::mkpath() << appImageRoot
 165                 << _T("runtime"))
 166         .createJvmLauncher());
 167 
 168     const DllWrapper jliDll(jvm->getPath());
 169     std::unique_ptr<DllWrapper> splashDll;
 170     if (jvm->isWithSplash()) {
 171         const DllWrapper jvmDll(getJvmLibPath(*jvm, jvm->isClientJvm()));
 172         splashDll = std::unique_ptr<DllWrapper>(new DllWrapper(
 173                 FileUtils::mkpath() 
 174                         << FileUtils::dirname(jvm->getPath()) 
 175                         << _T("splashscreen.dll")));
 176     }
 177 
 178     jvm->launch();
 179 }
 180 
 181 } // namespace
 182 
 183 #ifndef JP_LAUNCHERW
 184 
 185 int __cdecl  wmain() {
 186     return AppLauncher::launch(std::nothrow, launchApp);
 187 }
 188 
 189 #else
 190 
 191 namespace {
 192 
 193 class LastErrorGuiLogAppender : public LogAppender {
 194 public:
 195     virtual void append(const LogEvent& v) {
 196         JP_TRY;
 197 
 198         const std::wstring msg = (tstrings::any()
 199                 << AppLauncher::lastErrorMsg()).wstr();
 200         MessageBox(0, msg.c_str(),
 201             FileUtils::basename(SysInfo::getProcessModulePath()).c_str(),
 202             MB_ICONERROR | MB_OK);
 203 
 204         JP_CATCH_ALL;
 205     }
 206 };
 207 
 208 
 209 class Console {
 210 public:
 211     Console() {
 212         if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
 213             // Failed to connect to parent's console. Create our own.
 214             if (!AllocConsole()) {
 215                 // We already have a console, no need to redirect std I/O.
 216                 return;
 217             }
 218         }
 219 
 220         stdoutChannel = std::unique_ptr<Channel>(new Channel(stdout));
 221         stderrChannel = std::unique_ptr<Channel>(new Channel(stderr));
 222     }
 223 
 224     struct FileCloser {
 225         typedef FILE* pointer;
 226 
 227         void operator()(pointer h) {
 228             ::fclose(h);
 229         }
 230     };
 231 
 232     typedef std::unique_ptr<
 233         FileCloser::pointer,
 234         FileCloser
 235     > UniqueFILEHandle;
 236 
 237 private:
 238     class Channel {
 239     public:
 240         Channel(FILE* stdFILEHandle): stdFILEHandle(stdFILEHandle) {
 241             const char* stdFileName = "CONOUT$";
 242             const char* openMode = "w";
 243             if (stdFILEHandle == stdin) {
 244                 stdFileName = "CONIN$";
 245                 openMode = "r";
 246             }
 247 
 248             FILE* fp = 0;
 249             freopen_s(&fp, stdFileName, openMode, stdFILEHandle);
 250 
 251             fileHandle = UniqueFILEHandle(fp);
 252 
 253             std::ios_base::sync_with_stdio();
 254         }
 255 
 256         virtual ~Channel() {
 257             JP_TRY;
 258 
 259             FILE* fp = 0;
 260             fileHandle = UniqueFILEHandle(fp);
 261             std::ios_base::sync_with_stdio();
 262 
 263             JP_CATCH_ALL;
 264         }
 265 
 266     private:
 267         UniqueFILEHandle fileHandle;
 268         FILE *stdFILEHandle;
 269     };
 270 
 271     std::unique_ptr<Channel> stdoutChannel;
 272     std::unique_ptr<Channel> stderrChannel;
 273 };
 274 
 275 
 276 void launchAppW() {
 277     std::unique_ptr<Console> console;
 278     if (AppLauncher::isWithLogging()) {
 279         console = std::unique_ptr<Console>(new Console());
 280     }
 281 
 282     launchApp();
 283 }
 284 
 285 } // namespace
 286 
 287 
 288 int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) {
 289     LastErrorGuiLogAppender lastErrorLogAppender;
 290     TeeLogAppender logAppender(&AppLauncher::defaultLastErrorLogAppender(),
 291             &lastErrorLogAppender);
 292     return AppLauncher::launch(std::nothrow, launchAppW, &logAppender);
 293 }
 294 
 295 #endif