From 98982a60fba32ae3ffa66ffc7a1fd9f066a35287 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Wed, 1 Aug 2012 13:26:04 +0400 Subject: [PATCH] spectrum win32 service, in progress --- .gitignore | 3 +- CMakeLists.txt | 4 - msvc-deps/CMakeLists.txt | 1 - spectrum/src/CMakeLists.txt | 8 + spectrum/src/main.cpp | 48 +++++- spectrum/src/win32/SpectrumService.cpp | 19 +++ spectrum/src/win32/SpectrumService.h | 14 ++ spectrum/src/win32/WindowsService.cpp | 214 +++++++++++++++++++++++++ spectrum/src/win32/WindowsService.h | 58 +++++++ src/config.cpp | 3 +- 10 files changed, 364 insertions(+), 8 deletions(-) create mode 100644 spectrum/src/win32/SpectrumService.cpp create mode 100644 spectrum/src/win32/SpectrumService.h create mode 100644 spectrum/src/win32/WindowsService.cpp create mode 100644 spectrum/src/win32/WindowsService.h diff --git a/.gitignore b/.gitignore index 0adee0ba..1df37d1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.pb.cc -*.pb.h \ No newline at end of file +*.pb.h +plugin/python/protocol_pb2.py \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ec59679c..c01c3072 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,12 +59,8 @@ endif() find_package(Boost COMPONENTS program_options date_time system filesystem regex signals REQUIRED) message( STATUS "Found Boost: ${Boost_LIBRARIES}, ${Boost_INCLUDE_DIR}") -if (CMAKE_COMPILER_IS_GNUCXX) set(Protobuf_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") find_package(Protobuf REQUIRED) -else() -set(PROTOBUF_FOUND TRUE) -endif() set(Communi_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") find_package(Communi) diff --git a/msvc-deps/CMakeLists.txt b/msvc-deps/CMakeLists.txt index 06fb4159..e830d462 100644 --- a/msvc-deps/CMakeLists.txt +++ b/msvc-deps/CMakeLists.txt @@ -1,2 +1 @@ ADD_SUBDIRECTORY(sqlite3) -ADD_SUBDIRECTORY(protobuf) \ No newline at end of file diff --git a/spectrum/src/CMakeLists.txt b/spectrum/src/CMakeLists.txt index b0e9d42e..a9d7413c 100644 --- a/spectrum/src/CMakeLists.txt +++ b/spectrum/src/CMakeLists.txt @@ -1,7 +1,15 @@ cmake_minimum_required(VERSION 2.6) FILE(GLOB SRC *.cpp) +if (WIN32) +FILE(GLOB WIN_SRC win32/*.cpp) +include_directories(win32) +ADD_EXECUTABLE(spectrum2 ${SRC} ${WIN_SRC}) +else() ADD_EXECUTABLE(spectrum2 ${SRC}) +endif() + + ADD_DEPENDENCIES(spectrum2 spectrum2_libpurple_backend) ADD_DEPENDENCIES(spectrum2 spectrum2_libircclient-qt_backend) diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp index abff2382..6ac952ed 100644 --- a/spectrum/src/main.cpp +++ b/spectrum/src/main.cpp @@ -26,6 +26,7 @@ #else #include #define getpid _getpid +#include "win32/SpectrumService.h" #endif #include @@ -143,6 +144,11 @@ int main(int argc, char **argv) ("config", boost::program_options::value(&config_file)->default_value(""), "Config file") ("version,v", "Shows Spectrum version") ; +#ifdef WIN32 + desc.add_options() + ("install-service,i", "Install spectrum as Windows service") + ("uninstall-service,u", "Uninstall Windows service"); +#endif try { boost::program_options::positional_options_description p; @@ -170,6 +176,46 @@ int main(int argc, char **argv) if(vm.count("no-daemonize")) { no_daemon = true; } +#ifdef WIN32 + if (vm.count("install-service")) { + SpectrumService ntservice; + if (!ntservice.IsInstalled()) { + // determine the name of the currently executing file + char szFilePath[MAX_PATH]; + GetModuleFileName(NULL, szFilePath, sizeof(szFilePath)); + std::string exe_file(szFilePath); + std::string config_file = exe_file.replace(exe_file.end() - 4, exe_file.end(), ".cfg"); + std::string service_path = std::string(szFilePath) + std::string(" --config ") + config_file; + + if (ntservice.Install(service_path.c_str())) { + std::cout << "Successfully installed" << std::endl; + return 0; + } else { + std::cout << "Error installing service, are you an Administrator?" << std::endl; + return 1; + } + } else { + std::cout << "Already installed" << std::endl; + return 1; + } + } + if (vm.count("uninstall-service")) { + SpectrumService ntservice; + if (ntservice.IsInstalled()) { + if (ntservice.Remove()) { + std::cout << "Successfully removed" << std::endl; + return 0; + } else { + std::cout << "Error removing service, are you an Administrator?" << std::endl; + return 1; + } + } else { + std::cout << "Service not installed" << std::endl; + return 1; + } + } + +#endif } catch (std::runtime_error& e) { @@ -286,7 +332,7 @@ int main(int argc, char **argv) return -2; } } - else if (!storageBackend->connect()) { + else if (!storageBackend->connect()) { std::cerr << "Can't connect to database. Check the log to find out the reason.\n"; return -1; } diff --git a/spectrum/src/win32/SpectrumService.cpp b/spectrum/src/win32/SpectrumService.cpp new file mode 100644 index 00000000..1fbcacd3 --- /dev/null +++ b/spectrum/src/win32/SpectrumService.cpp @@ -0,0 +1,19 @@ +#include "SpectrumService.h" + +SpectrumService::SpectrumService(void) { + serviceName = "Spectrum2"; + displayName = "Spectrum2 XMPP Transport"; + username = NULL; + password = NULL; +} + +SpectrumService::~SpectrumService(void) {} + +void SpectrumService::Stop() { + ReportStatus((DWORD)SERVICE_STOP_PENDING); +} + +void SpectrumService::Run(DWORD argc, LPTSTR *argv) { + ReportStatus((DWORD)SERVICE_RUNNING); + main(argc, argv); +} \ No newline at end of file diff --git a/spectrum/src/win32/SpectrumService.h b/spectrum/src/win32/SpectrumService.h new file mode 100644 index 00000000..404b1057 --- /dev/null +++ b/spectrum/src/win32/SpectrumService.h @@ -0,0 +1,14 @@ +#include +#include "WindowsService.h" + +class SpectrumService : public WindowsService { + +public: + SpectrumService(void); + ~SpectrumService(void); +protected: + void Stop(); + void Run(DWORD argc, LPTSTR *argv); +}; + +int main(int argc, char **argv); \ No newline at end of file diff --git a/spectrum/src/win32/WindowsService.cpp b/spectrum/src/win32/WindowsService.cpp new file mode 100644 index 00000000..0a749378 --- /dev/null +++ b/spectrum/src/win32/WindowsService.cpp @@ -0,0 +1,214 @@ +/* Copyright (C) 2005 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include +#include +#include ".\windowsservice.h" + +static WindowsService *gService; + +WindowsService::WindowsService(void) : + statusCheckpoint(0), + serviceName(NULL), + inited(false), + dwAcceptedControls(SERVICE_ACCEPT_STOP), + debugging(false) +{ + gService= this; + status.dwServiceType= SERVICE_WIN32_OWN_PROCESS; + status.dwServiceSpecificExitCode= 0; +} + +WindowsService::~WindowsService(void) +{ +} + +BOOL WindowsService::Install(const char *szFilePath) +{ + bool ret_val= false; + SC_HANDLE newService; + SC_HANDLE scm; + + if (IsInstalled()) return true; + + // open a connection to the SCM + if (!(scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE))) + return false; + + newService= CreateService(scm, serviceName, displayName, + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, + szFilePath, NULL, NULL, NULL, username, + password); + + if (newService) + { + CloseServiceHandle(newService); + ret_val= true; + } + + CloseServiceHandle(scm); + return ret_val; +} + +BOOL WindowsService::Init() +{ + assert(serviceName != NULL); + + if (inited) return true; + + SERVICE_TABLE_ENTRY stb[] = + { + { (LPSTR)serviceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain}, + { NULL, NULL } + }; + inited= true; + return StartServiceCtrlDispatcher(stb); //register with the Service Manager +} + +BOOL WindowsService::Remove() +{ + bool ret_val= false; + + if (! IsInstalled()) + return true; + + // open a connection to the SCM + SC_HANDLE scm= OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE); + if (! scm) + return false; + + SC_HANDLE service= OpenService(scm, serviceName, DELETE); + if (service) + { + if (DeleteService(service)) + ret_val= true; + DWORD dw= ::GetLastError(); + CloseServiceHandle(service); + } + + CloseServiceHandle(scm); + return ret_val; +} + +BOOL WindowsService::IsInstalled() +{ + BOOL ret_val= FALSE; + + SC_HANDLE scm= ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + SC_HANDLE serv_handle= ::OpenService(scm, serviceName, SERVICE_QUERY_STATUS); + + ret_val= serv_handle != NULL; + + ::CloseServiceHandle(serv_handle); + ::CloseServiceHandle(scm); + + return ret_val; +} + +void WindowsService::SetAcceptedControls(DWORD acceptedControls) +{ + dwAcceptedControls= acceptedControls; +} + + +BOOL WindowsService::ReportStatus(DWORD currentState, DWORD waitHint, + DWORD dwError) +{ + if(debugging) return TRUE; + + if(currentState == SERVICE_START_PENDING) + status.dwControlsAccepted= 0; + else + status.dwControlsAccepted= dwAcceptedControls; + + status.dwCurrentState= currentState; + status.dwWin32ExitCode= dwError != 0 ? + ERROR_SERVICE_SPECIFIC_ERROR : NO_ERROR; + status.dwWaitHint= waitHint; + status.dwServiceSpecificExitCode= dwError; + + if(currentState == SERVICE_RUNNING || currentState == SERVICE_STOPPED) + { + status.dwCheckPoint= 0; + statusCheckpoint= 0; + } + else + status.dwCheckPoint= ++statusCheckpoint; + + // Report the status of the service to the service control manager. + BOOL result= SetServiceStatus(statusHandle, &status); + if (!result) + Log("ReportStatus failed"); + + return result; +} + +void WindowsService::RegisterAndRun(DWORD argc, LPTSTR *argv) +{ + statusHandle= ::RegisterServiceCtrlHandler(serviceName, ControlHandler); + if (statusHandle && ReportStatus(SERVICE_START_PENDING)) + Run(argc, argv); + ReportStatus(SERVICE_STOPPED); +} + +void WindowsService::HandleControlCode(DWORD opcode) +{ + // Handle the requested control code. + switch(opcode) { + case SERVICE_CONTROL_STOP: + // Stop the service. + status.dwCurrentState= SERVICE_STOP_PENDING; + Stop(); + break; + + case SERVICE_CONTROL_PAUSE: + status.dwCurrentState= SERVICE_PAUSE_PENDING; + Pause(); + break; + + case SERVICE_CONTROL_CONTINUE: + status.dwCurrentState= SERVICE_CONTINUE_PENDING; + Continue(); + break; + + case SERVICE_CONTROL_SHUTDOWN: + Shutdown(); + break; + + case SERVICE_CONTROL_INTERROGATE: + ReportStatus(status.dwCurrentState); + break; + + default: + // invalid control code + break; + } +} + +void WINAPI WindowsService::ServiceMain(DWORD argc, LPTSTR *argv) +{ + assert(gService != NULL); + + // register our service control handler: + gService->RegisterAndRun(argc, argv); +} + +void WINAPI WindowsService::ControlHandler(DWORD opcode) +{ + assert(gService != NULL); + + return gService->HandleControlCode(opcode); +} diff --git a/spectrum/src/win32/WindowsService.h b/spectrum/src/win32/WindowsService.h new file mode 100644 index 00000000..55e91d8b --- /dev/null +++ b/spectrum/src/win32/WindowsService.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2005 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#pragma once + +class WindowsService +{ +protected: + bool inited; + const char *serviceName; + const char *displayName; + const char *username; + const char *password; + SERVICE_STATUS_HANDLE statusHandle; + DWORD statusCheckpoint; + SERVICE_STATUS status; + DWORD dwAcceptedControls; + bool debugging; + +public: + WindowsService(void); + ~WindowsService(void); + + BOOL Install(const char *szFilePath); + BOOL Remove(); + BOOL Init(); + BOOL IsInstalled(); + void SetAcceptedControls(DWORD acceptedControls); + void Debug(bool debugFlag) { debugging= debugFlag; } + +public: + static void WINAPI ServiceMain(DWORD argc, LPTSTR *argv); + static void WINAPI ControlHandler(DWORD CtrlType); + +protected: + virtual void Run(DWORD argc, LPTSTR *argv)= 0; + virtual void Stop() {} + virtual void Shutdown() {} + virtual void Pause() {} + virtual void Continue() {} + virtual void Log(const char *msg) {} + + BOOL ReportStatus(DWORD currentStatus, DWORD waitHint= 3000, DWORD dwError=0); + void HandleControlCode(DWORD opcode); + void RegisterAndRun(DWORD argc, LPTSTR *argv); +}; diff --git a/src/config.cpp b/src/config.cpp index d33d7d51..f9b2a103 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -55,12 +55,13 @@ bool Config::load(const std::string &configfile, boost::program_options::options m_file = configfile; bool ret = load(ifs, opts, jid); ifs.close(); - +#ifndef WIN32 char path[PATH_MAX] = ""; if (m_file.find_first_of("/") != 0) { getcwd(path, PATH_MAX); m_file = std::string(path) + "/" + m_file; } +#endif return ret; }