diff --git a/daemon/main.cpp b/daemon/main.cpp index f1bee4d93..16021ae0b 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -43,7 +43,7 @@ operator delete(void* ptr, size_t) noexcept #ifdef _WIN32 #include -#include +#include extern "C" LONG FAR PASCAL win32_signal_handler(EXCEPTION_POINTERS*); extern "C" VOID FAR PASCAL @@ -51,7 +51,8 @@ win32_daemon_entry(DWORD, LPTSTR*); VOID ReportSvcStatus(DWORD,DWORD,DWORD); jmp_buf svc_entry; SERVICE_STATUS SvcStatus; -SERVICE_STATUS_HANDLE SvcStatusHandle; +SERVICE_STATUS_HANDLE SvcStatusHandle; +bool start_as_daemon = false; #endif std::shared_ptr ctx; @@ -92,101 +93,96 @@ handle_signal_win32(DWORD fdwCtrlType) void install_win32_daemon() { - SC_HANDLE schSCManager; - SC_HANDLE schService; - TCHAR szPath[MAX_PATH]; + SC_HANDLE schSCManager; + SC_HANDLE schService; + std::array szPath{}; - if( !GetModuleFileName( nullptr, szPath, MAX_PATH ) ) - { - llarp::LogError("Cannot install service ", GetLastError()); - return; - } + if( !GetModuleFileName( nullptr, szPath.data(), MAX_PATH ) ) + { + llarp::LogError("Cannot install service ", GetLastError()); + return; + } + StringCchCat(szPath.data(), 1024, " --win32-daemon"); - // Get a handle to the SCM database. - - schSCManager = OpenSCManager( - nullptr, // local computer - nullptr, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights + // Get a handle to the SCM database. + schSCManager = OpenSCManager( + nullptr, // local computer + nullptr, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights - if (nullptr == schSCManager) - { - llarp::LogError("OpenSCManager failed ", GetLastError()); - return; - } + if (nullptr == schSCManager) + { + llarp::LogError("OpenSCManager failed ", GetLastError()); + return; + } - // Create the service - - schService = CreateService( - schSCManager, // SCM database - "lokinet", // name of service - "Lokinet for Windows", // service name to display - SERVICE_ALL_ACCESS, // desired access - SERVICE_WIN32_OWN_PROCESS, // service type - SERVICE_DEMAND_START, // start type - SERVICE_ERROR_NORMAL, // error control type - szPath, // path to service's binary - nullptr, // no load ordering group - nullptr, // no tag identifier - nullptr, // no dependencies - nullptr, // LocalSystem account - nullptr); // no password + // Create the service + schService = CreateService( + schSCManager, // SCM database + "lokinet", // name of service + "Lokinet for Windows", // service name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_DEMAND_START, // start type + SERVICE_ERROR_NORMAL, // error control type + szPath.data(), // path to service's binary + nullptr, // no load ordering group + nullptr, // no tag identifier + nullptr, // no dependencies + nullptr, // LocalSystem account + nullptr); // no password - if (schService == nullptr) - { - llarp::LogError("CreateService failed ", GetLastError()); - CloseServiceHandle(schSCManager); - return; - } - else llarp::LogInfo("Service installed successfully"); - - CloseServiceHandle(schService); + if (schService == nullptr) + { + llarp::LogError("CreateService failed ", GetLastError()); CloseServiceHandle(schSCManager); + return; + } + else llarp::LogInfo("Service installed successfully"); + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); } void uninstall_win32_daemon() { - SC_HANDLE schSCManager; - SC_HANDLE schService; - SERVICE_STATUS ssStatus; - - // Get a handle to the SCM database. + SC_HANDLE schSCManager; + SC_HANDLE schService; + + // Get a handle to the SCM database. + schSCManager = OpenSCManager( + nullptr, // local computer + nullptr, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights - schSCManager = OpenSCManager( - nullptr, // local computer - nullptr, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights - - if (nullptr == schSCManager) - { - llarp::LogError("OpenSCManager failed ", GetLastError()); - return; - } - - // Get a handle to the service. + if (nullptr == schSCManager) + { + llarp::LogError("OpenSCManager failed ", GetLastError()); + return; + } - schService = OpenService( - schSCManager, // SCM database - "lokinet", // name of service - 0x10000); // need delete access + // Get a handle to the service. + schService = OpenService( + schSCManager, // SCM database + "lokinet", // name of service + 0x10000); // need delete access - if (schService == nullptr) - { - llarp::LogError("OpenService failed ", GetLastError()); - CloseServiceHandle(schSCManager); - return; - } + if (schService == nullptr) + { + llarp::LogError("OpenService failed ", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } - // Delete the service. - - if (! DeleteService(schService) ) - { - llarp::LogError("DeleteService failed ", GetLastError()); - } - else llarp::LogInfo("Service deleted successfully\n"); + // Delete the service. + if (! DeleteService(schService) ) + { + llarp::LogError("DeleteService failed ", GetLastError()); + } + else llarp::LogInfo("Service deleted successfully\n"); - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); } #endif @@ -246,6 +242,11 @@ main(int argc, char* argv[]) return -1; SetConsoleCtrlHandler(handle_signal_win32, TRUE); // SetUnhandledExceptionFilter(win32_signal_handler); + SERVICE_TABLE_ENTRY DispatchTable[] = + { + { "lokinet", (LPSERVICE_MAIN_FUNCTION) win32_daemon_entry }, + { NULL, NULL } + }; #endif cxxopts::Options options( "lokinet", @@ -257,6 +258,7 @@ main(int argc, char* argv[]) #ifdef _WIN32 ("install", "install win32 daemon to SCM", cxxopts::value()) ("remove", "remove win32 daemon from SCM", cxxopts::value()) + ("win32-daemon", "do not use interactively", cxxopts::value()) #endif ("g,generate", "generate client config", cxxopts::value())( "r,relay", "run as relay instead of client", cxxopts::value())( @@ -302,17 +304,19 @@ main(int argc, char* argv[]) #ifdef _WIN32 if (result.count("install")) { - // install_win32_daemon(); - std::cout << "windows daemon coming soon(tm)" << std::endl; + install_win32_daemon(); return 0; } if (result.count("remove")) { - // uninstall_win32_daemon(); - std::cout << "windows daemon coming soon(tm)" << std::endl; + uninstall_win32_daemon(); return 0; } + if (result.count("win32-daemon")) + { + start_as_daemon = true; + } #endif if (result.count("generate") > 0) { @@ -351,8 +355,13 @@ main(int argc, char* argv[]) } #ifdef _WIN32 - setjmp(svc_entry); - ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); + if (start_as_daemon) + { + setjmp(svc_entry); + // calling this twice returns a harmless error (daemon already running) + StartServiceCtrlDispatcher(DispatchTable); + ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); + } #endif if (!configFile.empty()) @@ -503,6 +512,7 @@ VOID FAR PASCAL SvcCtrlHandler(DWORD dwCtrl) // The win32 daemon entry point is just a trampoline that returns control // to the original lokinet entry +// and only gets called if we get --win32-daemon in the command line VOID FAR PASCAL win32_daemon_entry(DWORD largc, LPTSTR* largv) { UNREFERENCED_PARAMETER(largc);