Windows Service Support

This commit is contained in:
Vitaly Takmazov 2012-11-10 02:59:46 +04:00
parent 3aff761db6
commit a78794c2e7
8 changed files with 191 additions and 339 deletions

View file

@ -1,13 +1,13 @@
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()
if (WIN32)
FILE(GLOB WIN_SRC win32/*.cpp)
include_directories(win32)
ADD_EXECUTABLE(spectrum2 ${SRC} ${WIN_SRC})
else()
ADD_EXECUTABLE(spectrum2 ${SRC})
# endif()
endif()

View file

@ -33,7 +33,7 @@
#else
#include <process.h>
#define getpid _getpid
// #include "win32/SpectrumService.h"
#include "win32/ServiceWrapper.h"
#endif
#include <sys/stat.h>
@ -45,6 +45,13 @@ DEFINE_LOGGER(logger, "Spectrum");
Swift::SimpleEventLoop *eventLoop_ = NULL;
Component *component_ = NULL;
UserManager *userManager_ = NULL;
Config *config_ = NULL;
void stop() {
userManager_->removeAllUsers(false);
component_->stop();
eventLoop_->stop();
}
static void stop_spectrum() {
userManager_->removeAllUsers(false);
@ -125,7 +132,7 @@ static void daemonize(const char *cwd, const char *lock_file) {
int main(int argc, char **argv)
{
Config config(argc, argv);
config_ = &config;
boost::program_options::variables_map vm;
bool no_daemon = false;
std::string config_file;
@ -160,9 +167,9 @@ int main(int argc, char **argv)
("version,v", "Shows Spectrum version")
;
#ifdef WIN32
// desc.add_options()
// ("install-service,i", "Install spectrum as Windows service")
// ("uninstall-service,u", "Uninstall Windows service");
desc.add_options()
("install-service,i", "Install spectrum as Windows service")
("uninstall-service,u", "Uninstall Windows service");
#endif
try
{
@ -192,18 +199,17 @@ int main(int argc, char **argv)
no_daemon = true;
}
#ifdef WIN32
#if 0
if (vm.count("install-service")) {
SpectrumService ntservice;
ServiceWrapper ntservice("Spectrum2");
if (!ntservice.IsInstalled()) {
// determine the name of the currently executing file
char szFilePath[MAX_PATH];
GetModuleFileName(NULL, szFilePath, sizeof(szFilePath));
GetModuleFileNameA(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())) {
if (ntservice.Install((char *)service_path.c_str())) {
std::cout << "Successfully installed" << std::endl;
return 0;
} else {
@ -216,9 +222,9 @@ int main(int argc, char **argv)
}
}
if (vm.count("uninstall-service")) {
SpectrumService ntservice;
ServiceWrapper ntservice("Spectrum2");
if (ntservice.IsInstalled()) {
if (ntservice.Remove()) {
if (ntservice.UnInstall()) {
std::cout << "Successfully removed" << std::endl;
return 0;
} else {
@ -229,8 +235,7 @@ int main(int argc, char **argv)
std::cout << "Service not installed" << std::endl;
return 1;
}
}
#endif
}
#endif
}
catch (std::runtime_error& e)
@ -310,36 +315,49 @@ int main(int argc, char **argv)
// removeOldIcons(CONFIG_STRING(&config, "service.working_dir") + "/icons");
}
#endif
#ifdef WIN32
ServiceWrapper ntservice("Spectrum2");
if (ntservice.IsInstalled()) {
ntservice.RunService();
} else {
mainloop();
}
#else
mainloop();
#endif
}
Logging::initMainLogging(&config);
int mainloop() {
Logging::initMainLogging(config_);
#ifndef WIN32
if (!CONFIG_STRING(&config, "service.group").empty() ||!CONFIG_STRING(&config, "service.user").empty() ) {
if (!CONFIG_STRING(config_, "service.group").empty() ||!CONFIG_STRING(config_, "service.user").empty() ) {
struct rlimit limit;
getrlimit(RLIMIT_CORE, &limit);
if (!CONFIG_STRING(&config, "service.group").empty()) {
if (!CONFIG_STRING(config_, "service.group").empty()) {
struct group *gr;
if ((gr = getgrnam(CONFIG_STRING(&config, "service.group").c_str())) == NULL) {
std::cerr << "Invalid service.group name " << CONFIG_STRING(&config, "service.group") << "\n";
if ((gr = getgrnam(CONFIG_STRING(config_, "service.group").c_str())) == NULL) {
std::cerr << "Invalid service.group name " << CONFIG_STRING(config_, "service.group") << "\n";
return 1;
}
if (((setgid(gr->gr_gid)) != 0) || (initgroups(CONFIG_STRING(&config, "service.user").c_str(), gr->gr_gid) != 0)) {
std::cerr << "Failed to set service.group name " << CONFIG_STRING(&config, "service.group") << " - " << gr->gr_gid << ":" << strerror(errno) << "\n";
if (((setgid(gr->gr_gid)) != 0) || (initgroups(CONFIG_STRING(config_, "service.user").c_str(), gr->gr_gid) != 0)) {
std::cerr << "Failed to set service.group name " << CONFIG_STRING(config_, "service.group") << " - " << gr->gr_gid << ":" << strerror(errno) << "\n";
return 1;
}
}
if (!CONFIG_STRING(&config, "service.user").empty()) {
if (!CONFIG_STRING(config_, "service.user").empty()) {
struct passwd *pw;
if ((pw = getpwnam(CONFIG_STRING(&config, "service.user").c_str())) == NULL) {
std::cerr << "Invalid service.user name " << CONFIG_STRING(&config, "service.user") << "\n";
if ((pw = getpwnam(CONFIG_STRING(config_, "service.user").c_str())) == NULL) {
std::cerr << "Invalid service.user name " << CONFIG_STRING(config_, "service.user") << "\n";
return 1;
}
if ((setuid(pw->pw_uid)) != 0) {
std::cerr << "Failed to set service.user name " << CONFIG_STRING(&config, "service.user") << " - " << pw->pw_uid << ":" << strerror(errno) << "\n";
std::cerr << "Failed to set service.user name " << CONFIG_STRING(config_, "service.user") << " - " << pw->pw_uid << ":" << strerror(errno) << "\n";
return 1;
}
}
@ -355,14 +373,14 @@ int main(int argc, char **argv)
Swift::SimpleEventLoop eventLoop;
Swift::BoostNetworkFactories *factories = new Swift::BoostNetworkFactories(&eventLoop);
UserRegistry userRegistry(&config, factories);
UserRegistry userRegistry(config_, factories);
Component transport(&eventLoop, factories, &config, NULL, &userRegistry);
Component transport(&eventLoop, factories, config_, NULL, &userRegistry);
component_ = &transport;
// Logger logger(&transport);
std::string error;
StorageBackend *storageBackend = StorageBackend::createBackend(&config, error);
StorageBackend *storageBackend = StorageBackend::createBackend(config_, error);
if (storageBackend == NULL) {
if (!error.empty()) {
std::cerr << error << "\n";
@ -393,7 +411,7 @@ int main(int argc, char **argv)
FileTransferManager ftManager(&transport, &userManager);
NetworkPluginServer plugin(&transport, &config, &userManager, &ftManager, &discoItemsResponder);
NetworkPluginServer plugin(&transport, config_, &userManager, &ftManager, &discoItemsResponder);
plugin.start();
AdminInterface adminInterface(&transport, &userManager, &plugin, storageBackend, userRegistration);

View file

@ -0,0 +1,120 @@
#include "ServiceWrapper.h"
LPSTR ServiceName;
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE ServiceStatusHandle;
bool doAction (int action, int desiredAccess, LPSTR path = NULL);
enum actions {
DO_INSTALL, DO_DELETE, DO_CHECK
};
ServiceWrapper::ServiceWrapper(LPSTR serviceName)
{
ServiceName = serviceName;
ServiceStatusHandle = 0;
}
ServiceWrapper::~ServiceWrapper(void)
{
}
bool ServiceWrapper::Install(LPSTR commandLine) {
return doAction(DO_INSTALL, SC_MANAGER_ALL_ACCESS, commandLine);
}
bool ServiceWrapper::UnInstall() {
return doAction(DO_DELETE, SC_MANAGER_ALL_ACCESS);
}
bool ServiceWrapper::IsInstalled() {
return doAction(DO_CHECK, SC_MANAGER_CONNECT);
}
bool doAction(int action, int desiredAccess, LPSTR path) {
SC_HANDLE scm = OpenSCManager(NULL, NULL, desiredAccess);
SC_HANDLE service = NULL;
if (!scm) return FALSE;
switch(action) {
case DO_INSTALL:
service = CreateServiceA(
scm,
ServiceName,
ServiceName,
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
path,
NULL,
NULL,
NULL,
NULL,
NULL
);
return (service != NULL);
break;
case DO_DELETE:
service = OpenServiceA(scm, ServiceName, DELETE);
if (service == NULL)
return FALSE;
if (DeleteService(service))
return TRUE;
break;
case DO_CHECK:
service = OpenServiceA(scm, ServiceName, SERVICE_QUERY_STATUS);
return (service != NULL);
default:
return FALSE;
}
CloseServiceHandle(service);
CloseServiceHandle(scm);
return FALSE;
}
void WINAPI ServiceControlHandler(DWORD controlCode) {
switch (controlCode) {
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_STOP:
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
break;
default:
break;
}
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
stop();
}
void WINAPI ServiceMain(DWORD argc, LPSTR *argv) {
ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ServiceStatus.dwServiceSpecificExitCode = NO_ERROR;
ServiceStatusHandle = RegisterServiceCtrlHandlerA(ServiceName, ServiceControlHandler);
if (ServiceStatusHandle) {
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
ServiceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
mainloop();
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
ServiceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
}
}
void ServiceWrapper::RunService() {
SERVICE_TABLE_ENTRYA serviceTable[] = {
{ ServiceName, ServiceMain },
{ NULL, NULL}
};
StartServiceCtrlDispatcherA(serviceTable);
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "transport/config.h"
#include <windows.h>
#include <tchar.h>
class ServiceWrapper
{
public:
ServiceWrapper(LPSTR serviceName);
~ServiceWrapper(void);
bool Install(LPSTR commandLine);
bool UnInstall();
bool IsInstalled();
void RunService();
};
int mainloop();
void stop();

View file

@ -1,19 +0,0 @@
#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);
}

View file

@ -1,14 +0,0 @@
#include <windows.h>
#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);

View file

@ -1,214 +0,0 @@
/* 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 <windows.h>
#include <assert.h>
#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);
}

View file

@ -1,58 +0,0 @@
/* 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);
};