remote/main.cpp (207 lines of code) (raw):

#ifdef WIN32 #include <windows.h> #include "windows/PipeTransportServer.h" #include <thread> #include <TlHelp32.h> #endif //WIN32 #include <thrift/server/TThreadedServer.h> #include <thrift/transport/TServerSocket.h> #include <thrift/transport/TTransportUtils.h> #include "CefUtils.h" #include "ServerApplication.h" #include "ServerHandler.h" #include "log/Log.h" #include <boost/date_time/posix_time/posix_time.hpp> #include <filesystem> #include "CrashHandler.h" using namespace apache::thrift; using namespace apache::thrift::transport; using namespace apache::thrift::server; using namespace thrift_codegen; #ifdef OS_MAC extern void initMacApplication(); #else #include "include/cef_app.h" #include "handlers/app/HelperApp.h" #endif #ifndef NDEBUG #if defined(OS_WIN) void waitForDebug() { // Likely the call to // https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-isdebuggerpresent // can help printf("Waiting for debugger is not supported on windows"); } #elif defined(OS_LINUX) void waitForDebug() { printf("Waiting for debugger is not supported on linux"); // Probably something like this could work: // bool isDebuggerAttached() { // std::ifstream statusFile("/proc/self/status"); // std::string line; // while (std::getline(statusFile, line)) { // if (line.compare(0, 11, "TracerPid:") == 0) { // // Extract the tracer PID value // int tracerPid = std::stoi(line.substr(11)); // return tracerPid != 0; // } // } // return false; // } } #elif defined(OS_MAC) #include <sys/types.h> #include <sys/sysctl.h> bool isDebuggerAttached() { int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = getpid(); kinfo_proc info; info.kp_proc.p_flag = 0; size_t size = sizeof(info); if (sysctl(mib, 4, &info, &size, nullptr, 0) == -1) { perror("sysctl failure"); return false; } return (info.kp_proc.p_flag & P_TRACED) != 0; } void waitForDebug() { printf("Waiting for debugger(PID=%d)...", getpid()); while (!isDebuggerAttached()) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } printf("Attached"); } #endif #endif #ifdef WIN32 DWORD GetParentProcessPid() { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) return 0; PROCESSENTRY32 processEntry = {}; processEntry.dwSize = sizeof(PROCESSENTRY32); if (Process32First(hSnapshot, &processEntry)) { DWORD CurrentProcessId = GetCurrentProcessId(); do { if (processEntry.th32ProcessID == CurrentProcessId) break; } while (Process32Next(hSnapshot, &processEntry)); } CloseHandle(hSnapshot); return processEntry.th32ParentProcessID; } #endif int main(int argc, char* argv[]) { const boost::posix_time::ptime t0 = boost::posix_time::microsec_clock::local_time(); Log::setThreadName("main"); #if defined(OS_LINUX) CefRefPtr<CefApp> cefApp = nullptr; CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine(); command_line->InitFromArgv(argc, argv); const std::string& process_type = command_line->GetSwitchValue("type"); if (process_type == "renderer" || process_type == "zygote") cefApp = new HelperApp(); // On Linux the zygote process is used to spawn other process types. Since // we don't know what type of process it will be give it the renderer // client. CefMainArgs main_args(argc, argv); int exit_code = CefExecuteProcess(main_args, cefApp, nullptr); if (exit_code >= 0) { return exit_code; } #elif WIN32 // Execute subprocess (if necessary) CefRefPtr<CefApp> cefApp = nullptr; CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine(); command_line->InitFromString(::GetCommandLineW()); const std::string& process_type = command_line->GetSwitchValue("type"); const bool isMainBrowserProcess = process_type.empty(); if (!isMainBrowserProcess) { // Initialize watchdog thread. DWORD parentProcessPid = GetParentProcessPid(); HANDLE hParentProcess = OpenProcess(SYNCHRONIZE, FALSE, parentProcessPid); std::thread([hParentProcess]() { WaitForSingleObject(hParentProcess, INFINITE); ExitProcess(0); }).detach(); } if (process_type == "renderer") cefApp = new HelperApp(); CefMainArgs main_args(GetModuleHandle(0)); const int result = CefExecuteProcess(main_args, cefApp, nullptr); if (result >= 0) { // If CefExecuteProcess called for the browser process (identified by no "type" command-line value) // it will return immediately with a value of -1. return result; } #elif OS_MAC initMacApplication(); #endif const boost::posix_time::ptime t1 = boost::posix_time::microsec_clock::local_time(); fprintf(stdout, "Starting cer server. Pre-initialize spent %d ms.\n", (int)(t1 - t0).total_milliseconds()); ServerApplication& app = ServerApplication::instance(); if (!app.init(argc, argv)) { // Can't load CEF framework library. return 100; } if (!getBoolEnv("CEF_SERVER_DONT_CATCH_SIGNALS")) { setupCrashHandler(); } const CommandLineArgs& cmdArgs = app.getCmdArgs(); #ifndef NDEBUG if (cmdArgs.waitDebugger()) { waitForDebug(); } #endif boost::posix_time::ptime t2 = boost::posix_time::microsec_clock::local_time(); Log::trace("Start CEF initialization. ServerApplication initialization spent %d ms.", (t2 - t1).total_milliseconds()); const bool success = CefUtils::initializeCef(); if (!success) { Log::error("Cef initialization failed"); return 101; } if (!getBoolEnv("CEF_SERVER_DONT_CATCH_SIGNALS")) { setupCrashHandler(); } const boost::posix_time::ptime t3 = boost::posix_time::microsec_clock::local_time(); Log::trace("Create server transport. CEF initialization spent %d ms.", (t3 - t2).total_milliseconds()); std::shared_ptr<TServerTransport> serverTransport; if (cmdArgs.useTcp()) { serverTransport = std::make_shared<TServerSocket>("127.0.0.1", cmdArgs.getPort()); } else { std::string pipePath = cmdArgs.getPipe(); if (pipePath.empty()) { Log::error("Pipe path is empty, exit."); return 102; } #ifdef WIN32 if (pipePath.rfind("\\\\.\\pipe\\", 0) != 0) pipePath = "\\\\.\\pipe\\" + pipePath; Log::info("Windows-pipe transport will be used, path=%s", pipePath.c_str()); serverTransport = std::make_shared<PipeTransportServer>(pipePath); #else std::remove(pipePath.c_str()); serverTransport = std::make_shared<TServerSocket>(pipePath.c_str()); #endif //WIN32 } std::shared_ptr<apache::thrift::TProcessorFactory> processorFactory = app.getProcessorFactory(); std::shared_ptr<TThreadedServer> server = std::make_shared<TThreadedServer>( processorFactory, serverTransport, std::make_shared<TBufferedTransportFactory>(), std::make_shared<TBinaryProtocolFactory>()); const boost::posix_time::ptime t4 = boost::posix_time::microsec_clock::local_time(); Log::trace("Start listening thread. Transport initialization spent %d ms.", (t4 - t3).total_milliseconds()); std::thread servThread([=]() { Log::setThreadName("ServerListener"); try { server->serve(); } catch (TException& e) { Log::error("Exception in listening thread"); Log::error(e.what()); } catch (...) { Log::error("Unknown exception in listening thread"); } Log::debug("Done, server stopped."); }); const boost::posix_time::ptime t6 = boost::posix_time::microsec_clock::local_time(); Log::trace("Run CEF loop. Total initialization time %d ms.", (t6 - t0).total_milliseconds()); CefUtils::runCefLoop(); Log::debug("Finished message loop."); server->stop(); servThread.join(); app.onBeforeExit(); #ifndef WIN32 if (!cmdArgs.useTcp()) std::remove(cmdArgs.getPipe().c_str()); #endif // WIN32 if (cmdArgs.deleteRootCacheDir() && !app.isDefaultRoot()) { Log::debug("Remove root cache dir '%s'", app.getRootPath().c_str()); try { std::filesystem::remove_all(app.getRootPath()); } catch (const std::filesystem::filesystem_error& ex) { Log::error("Failed to remove root cache dir '%s'. Error: %s. Error code: %s", app.getRootPath().c_str(), ex.what(), ex.code().message().c_str()); } } Log::debug("Buy [%s]!", cmdArgs.getTransportDesc().c_str()); return 0; }