Browse Source

Merge pull request #67 from chertov/master

add mikhail4021 Win32Service, run i2pd as admin
pull/70/head
Meeh 11 years ago
parent
commit
75ddb26c18
  1. 5
      Win32/.gitignore
  2. 526
      Win32/Win32Service.cpp
  3. 89
      Win32/Win32Service.h
  4. 4
      Win32/i2pd.vcxproj
  5. 9
      Win32/i2pd.vcxproj.filters
  6. 1
      Win32/install_service.bat
  7. 1
      Win32/uninstall_service.bat
  8. 6
      i2p.cpp

5
Win32/.gitignore vendored

@ -1,6 +1,11 @@
* *
!*/ !*/
!*.h
!*.cpp
!*.bat
!*.sln !*.sln
!*.vcproj !*.vcproj
!*.vcxproj !*.vcxproj

526
Win32/Win32Service.cpp

@ -0,0 +1,526 @@
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS // to use freopen
#endif
#include "Win32Service.h"
#include <assert.h>
#include <strsafe.h>
#include <windows.h>
#include "Log.h"
#include "Transports.h"
#include "NTCPSession.h"
#include "Tunnel.h"
#include "NetDb.h"
#include "Garlic.h"
#include "util.h"
#include "Streaming.h"
I2PService *I2PService::s_service = NULL;
BOOL I2PService::Run(I2PService &service)
{
s_service = &service;
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ service.m_name, ServiceMain },
{ NULL, NULL }
};
return StartServiceCtrlDispatcher(serviceTable);
}
void WINAPI I2PService::ServiceMain(DWORD dwArgc, PSTR *pszArgv)
{
assert(s_service != NULL);
s_service->m_statusHandle = RegisterServiceCtrlHandler(
s_service->m_name, ServiceCtrlHandler);
if (s_service->m_statusHandle == NULL)
{
throw GetLastError();
}
s_service->Start(dwArgc, pszArgv);
}
void WINAPI I2PService::ServiceCtrlHandler(DWORD dwCtrl)
{
switch (dwCtrl)
{
case SERVICE_CONTROL_STOP: s_service->Stop(); break;
case SERVICE_CONTROL_PAUSE: s_service->Pause(); break;
case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break;
case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break;
case SERVICE_CONTROL_INTERROGATE: break;
default: break;
}
}
I2PService::I2PService(PSTR pszServiceName,
BOOL fCanStop,
BOOL fCanShutdown,
BOOL fCanPauseContinue) : _httpServer(nullptr), _httpProxy(nullptr)
{
m_name = (pszServiceName == NULL) ? "" : pszServiceName;
m_statusHandle = NULL;
m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
m_status.dwCurrentState = SERVICE_START_PENDING;
DWORD dwControlsAccepted = 0;
if (fCanStop)
dwControlsAccepted |= SERVICE_ACCEPT_STOP;
if (fCanShutdown)
dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN;
if (fCanPauseContinue)
dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
m_status.dwControlsAccepted = dwControlsAccepted;
m_status.dwWin32ExitCode = NO_ERROR;
m_status.dwServiceSpecificExitCode = 0;
m_status.dwCheckPoint = 0;
m_status.dwWaitHint = 0;
m_fStopping = FALSE;
// Create a manual-reset event that is not signaled at first to indicate
// the stopped signal of the service.
m_hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hStoppedEvent == NULL)
{
throw GetLastError();
}
}
I2PService::~I2PService(void)
{
if (m_hStoppedEvent)
{
CloseHandle(m_hStoppedEvent);
m_hStoppedEvent = NULL;
}
}
void I2PService::Start(DWORD dwArgc, PSTR *pszArgv)
{
try
{
SetServiceStatus(SERVICE_START_PENDING);
OnStart(dwArgc, pszArgv);
SetServiceStatus(SERVICE_RUNNING);
}
catch (DWORD dwError)
{
LogPrint("Service Start", dwError);
SetServiceStatus(SERVICE_STOPPED, dwError);
}
catch (...)
{
LogPrint("Service failed to start.", EVENTLOG_ERROR_TYPE);
SetServiceStatus(SERVICE_STOPPED);
}
}
void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv)
{
LogPrint("CppWindowsService in OnStart",
EVENTLOG_INFORMATION_TYPE);
i2p::util::config::OptionParser(dwArgc, pszArgv);
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
i2p::context.OverrideNTCPAddress(i2p::util::config::GetCharArg("-host", "127.0.0.1"),
i2p::util::config::GetArg("-port", 17070));
_httpServer = new i2p::util::HTTPServer(i2p::util::config::GetArg("-httpport", 7070));
_httpServer->Start();
LogPrint("HTTPServer started", EVENTLOG_INFORMATION_TYPE);
i2p::data::netdb.Start();
LogPrint("NetDB started", EVENTLOG_INFORMATION_TYPE);
i2p::transports.Start();
LogPrint("Transports started", EVENTLOG_INFORMATION_TYPE);
i2p::tunnel::tunnels.Start();
LogPrint("Tunnels started", EVENTLOG_INFORMATION_TYPE);
i2p::garlic::routing.Start();
LogPrint("Routing started", EVENTLOG_INFORMATION_TYPE);
i2p::stream::StartStreaming();
LogPrint("Streaming started", EVENTLOG_INFORMATION_TYPE);
_httpProxy = new i2p::proxy::HTTPProxy(i2p::util::config::GetArg("-httpproxyport", 4446));
_httpProxy->Start();
LogPrint("Proxy started", EVENTLOG_INFORMATION_TYPE);
_worker = new std::thread(std::bind(&I2PService::WorkerThread, this));
}
void I2PService::WorkerThread()
{
while (!m_fStopping)
{
::Sleep(1000); // Simulate some lengthy operations.
}
// Signal the stopped event.
SetEvent(m_hStoppedEvent);
}
void I2PService::Stop()
{
DWORD dwOriginalState = m_status.dwCurrentState;
try
{
SetServiceStatus(SERVICE_STOP_PENDING);
OnStop();
SetServiceStatus(SERVICE_STOPPED);
}
catch (DWORD dwError)
{
LogPrint("Service Stop", dwError);
SetServiceStatus(dwOriginalState);
}
catch (...)
{
LogPrint("Service failed to stop.", EVENTLOG_ERROR_TYPE);
SetServiceStatus(dwOriginalState);
}
}
void I2PService::OnStop()
{
// Log a service stop message to the Application log.
LogPrint("CppWindowsService in OnStop", EVENTLOG_INFORMATION_TYPE);
_httpProxy->Stop();
LogPrint("HTTPProxy stoped", EVENTLOG_INFORMATION_TYPE);
delete _httpProxy;
i2p::stream::StopStreaming();
LogPrint("Streaming stoped", EVENTLOG_INFORMATION_TYPE);
i2p::garlic::routing.Stop();
LogPrint("Routing stoped", EVENTLOG_INFORMATION_TYPE);
i2p::tunnel::tunnels.Stop();
LogPrint("Tunnels stoped", EVENTLOG_INFORMATION_TYPE);
i2p::transports.Stop();
LogPrint("Transports stoped", EVENTLOG_INFORMATION_TYPE);
i2p::data::netdb.Stop();
LogPrint("NetDB stoped", EVENTLOG_INFORMATION_TYPE);
_httpServer->Stop();
LogPrint("HTTPServer stoped", EVENTLOG_INFORMATION_TYPE);
delete _httpServer;
m_fStopping = TRUE;
if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
{
throw GetLastError();
}
_worker->join();
delete _worker;
}
void I2PService::Pause()
{
try
{
SetServiceStatus(SERVICE_PAUSE_PENDING);
OnPause();
SetServiceStatus(SERVICE_PAUSED);
}
catch (DWORD dwError)
{
LogPrint("Service Pause", dwError);
SetServiceStatus(SERVICE_RUNNING);
}
catch (...)
{
LogPrint("Service failed to pause.", EVENTLOG_ERROR_TYPE);
SetServiceStatus(SERVICE_RUNNING);
}
}
void I2PService::OnPause()
{
}
void I2PService::Continue()
{
try
{
SetServiceStatus(SERVICE_CONTINUE_PENDING);
OnContinue();
SetServiceStatus(SERVICE_RUNNING);
}
catch (DWORD dwError)
{
LogPrint("Service Continue", dwError);
SetServiceStatus(SERVICE_PAUSED);
}
catch (...)
{
LogPrint("Service failed to resume.", EVENTLOG_ERROR_TYPE);
SetServiceStatus(SERVICE_PAUSED);
}
}
void I2PService::OnContinue()
{
}
void I2PService::Shutdown()
{
try
{
OnShutdown();
SetServiceStatus(SERVICE_STOPPED);
}
catch (DWORD dwError)
{
LogPrint("Service Shutdown", dwError);
}
catch (...)
{
LogPrint("Service failed to shut down.", EVENTLOG_ERROR_TYPE);
}
}
void I2PService::OnShutdown()
{
}
void I2PService::SetServiceStatus(DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
m_status.dwCurrentState = dwCurrentState;
m_status.dwWin32ExitCode = dwWin32ExitCode;
m_status.dwWaitHint = dwWaitHint;
m_status.dwCheckPoint =
((dwCurrentState == SERVICE_RUNNING) ||
(dwCurrentState == SERVICE_STOPPED)) ?
0 : dwCheckPoint++;
::SetServiceStatus(m_statusHandle, &m_status);
}
//*****************************************************************************
void FreeHandles(SC_HANDLE schSCManager, SC_HANDLE schService)
{
if (schSCManager)
{
CloseServiceHandle(schSCManager);
schSCManager = NULL;
}
if (schService)
{
CloseServiceHandle(schService);
schService = NULL;
}
}
void InstallService(PSTR pszServiceName,
PSTR pszDisplayName,
DWORD dwStartType,
PSTR pszDependencies,
PSTR pszAccount,
PSTR pszPassword)
{
char szPath[MAX_PATH];
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)) == 0)
{
printf("GetModuleFileName failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
// Open the local default service control manager database
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT |
SC_MANAGER_CREATE_SERVICE);
if (schSCManager == NULL)
{
printf("OpenSCManager failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
// Install the service into SCM by calling CreateService
schService = CreateService(
schSCManager, // SCManager database
pszServiceName, // Name of service
pszDisplayName, // Name to display
SERVICE_QUERY_STATUS, // Desired access
SERVICE_WIN32_OWN_PROCESS, // Service type
dwStartType, // Service start type
SERVICE_ERROR_NORMAL, // Error control type
szPath, // Service's binary
NULL, // No load ordering group
NULL, // No tag identifier
pszDependencies, // Dependencies
pszAccount, // Service running account
pszPassword // Password of the account
);
if (schService == NULL)
{
printf("CreateService failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
printf("%s is installed.\n", pszServiceName);
// Centralized cleanup for all allocated resources.
FreeHandles(schSCManager, schService);
}
void UninstallService(PSTR pszServiceName)
{
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
SERVICE_STATUS ssSvcStatus = {};
// Open the local default service control manager database
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (schSCManager == NULL)
{
printf("OpenSCManager failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
// Open the service with delete, stop, and query status permissions
schService = OpenService(schSCManager, pszServiceName, SERVICE_STOP |
SERVICE_QUERY_STATUS | DELETE);
if (schService == NULL)
{
printf("OpenService failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
// Try to stop the service
if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus))
{
printf("Stopping %s.", pszServiceName);
Sleep(1000);
while (QueryServiceStatus(schService, &ssSvcStatus))
{
if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING)
{
printf(".");
Sleep(1000);
}
else break;
}
if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED)
{
printf("\n%s is stopped.\n", pszServiceName);
}
else
{
printf("\n%s failed to stop.\n", pszServiceName);
}
}
// Now remove the service by calling DeleteService.
if (!DeleteService(schService))
{
printf("DeleteService failed w/err 0x%08lx\n", GetLastError());
FreeHandles(schSCManager, schService);
return;
}
printf("%s is removed.\n", pszServiceName);
// Centralized cleanup for all allocated resources.
FreeHandles(schSCManager, schService);
}
void service_control(int isDaemon)
{
std::string serviceControl = i2p::util::config::GetArg("-service", "none");
if (serviceControl == "install")
{
InstallService(
SERVICE_NAME, // Name of service
SERVICE_DISPLAY_NAME, // Name to display
SERVICE_START_TYPE, // Service start type
SERVICE_DEPENDENCIES, // Dependencies
SERVICE_ACCOUNT, // Service running account
SERVICE_PASSWORD // Password of the account
);
exit(0);
}
else if (serviceControl == "remove")
{
UninstallService(SERVICE_NAME);
exit(0);
}
else if (serviceControl != "none")
{
printf(" --service=install to install the service.\n");
printf(" --service=remove to remove the service.\n");
exit(0);
}
else if (isDaemon)
{
std::string logfile = i2p::util::filesystem::GetDataDir().string();
logfile.append("\\debug.log");
FILE* openResult = freopen(logfile.c_str(), "a", stdout);
if (!openResult)
{
exit(-17);
}
LogPrint("Service logging enabled.");
I2PService service(SERVICE_NAME);
if (!I2PService::Run(service))
{
LogPrint("Service failed to run w/err 0x%08lx\n", GetLastError());
}
exit(0);
}
}

89
Win32/Win32Service.h

@ -0,0 +1,89 @@
#ifndef WIN_32_SERVICE_H__
#define WIN_32_SERVICE_H__
#include "../HTTPServer.h"
#include "../HTTPProxy.h"
#include <thread>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef _WIN32
// Internal name of the service
#define SERVICE_NAME "i2pService"
// Displayed name of the service
#define SERVICE_DISPLAY_NAME "i2p router service"
// Service start options.
#define SERVICE_START_TYPE SERVICE_DEMAND_START
// List of service dependencies - "dep1\0dep2\0\0"
#define SERVICE_DEPENDENCIES ""
// The name of the account under which the service should run
#define SERVICE_ACCOUNT "NT AUTHORITY\\LocalService"
// The password to the service account name
#define SERVICE_PASSWORD NULL
#endif
class I2PService
{
public:
I2PService(PSTR pszServiceName,
BOOL fCanStop = TRUE,
BOOL fCanShutdown = TRUE,
BOOL fCanPauseContinue = FALSE);
virtual ~I2PService(void);
static BOOL Run(I2PService &service);
void Stop();
protected:
virtual void OnStart(DWORD dwArgc, PSTR *pszArgv);
virtual void OnStop();
virtual void OnPause();
virtual void OnContinue();
virtual void OnShutdown();
void SetServiceStatus(DWORD dwCurrentState,
DWORD dwWin32ExitCode = NO_ERROR,
DWORD dwWaitHint = 0);
private:
static void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv);
static void WINAPI ServiceCtrlHandler(DWORD dwCtrl);
void WorkerThread();
void Start(DWORD dwArgc, PSTR *pszArgv);
void Pause();
void Continue();
void Shutdown();
static I2PService* s_service;
PSTR m_name;
SERVICE_STATUS m_status;
SERVICE_STATUS_HANDLE m_statusHandle;
BOOL m_fStopping;
HANDLE m_hStoppedEvent;
i2p::util::HTTPServer* _httpServer;
i2p::proxy::HTTPProxy* _httpProxy;
std::thread* _worker;
};
void InstallService(PSTR pszServiceName,
PSTR pszDisplayName,
DWORD dwStartType,
PSTR pszDependencies,
PSTR pszAccount,
PSTR pszPassword);
void UninstallService(PSTR pszServiceName);
void service_control(int isDaemon);
#endif // WIN_32_SERVICE_H__

4
Win32/i2pd.vcxproj

@ -37,6 +37,7 @@
<ClCompile Include="..\TunnelPool.cpp" /> <ClCompile Include="..\TunnelPool.cpp" />
<ClCompile Include="..\UPnP.cpp" /> <ClCompile Include="..\UPnP.cpp" />
<ClCompile Include="..\util.cpp" /> <ClCompile Include="..\util.cpp" />
<ClCompile Include="Win32Service.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\AddressBook.h" /> <ClInclude Include="..\AddressBook.h" />
@ -71,6 +72,7 @@
<ClInclude Include="..\TunnelPool.h" /> <ClInclude Include="..\TunnelPool.h" />
<ClInclude Include="..\UPnP.h" /> <ClInclude Include="..\UPnP.h" />
<ClInclude Include="..\util.h" /> <ClInclude Include="..\util.h" />
<ClInclude Include="Win32Service.h" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<ProjectGuid>{930568EC-31C9-406A-AD1C-9636DF5D8FAA}</ProjectGuid> <ProjectGuid>{930568EC-31C9-406A-AD1C-9636DF5D8FAA}</ProjectGuid>
@ -136,6 +138,8 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>cryptlib.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>cryptlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(ProjectDir)$(TargetName)$(TargetExt)</OutputFile>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

9
Win32/i2pd.vcxproj.filters

@ -13,6 +13,9 @@
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter> </Filter>
<Filter Include="Win32">
<UniqueIdentifier>{a880a08c-16b8-4243-82ea-6bfc63bb7dab}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\Identity.cpp"> <ClCompile Include="..\Identity.cpp">
@ -93,6 +96,9 @@
<ClCompile Include="..\AddressBook.cpp"> <ClCompile Include="..\AddressBook.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Win32Service.cpp">
<Filter>Win32</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\Identity.h"> <ClInclude Include="..\Identity.h">
@ -191,5 +197,8 @@
<ClInclude Include="..\AddressBook.h"> <ClInclude Include="..\AddressBook.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Win32Service.h">
<Filter>Win32</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

1
Win32/install_service.bat

@ -0,0 +1 @@
i2pd --service=install

1
Win32/uninstall_service.bat

@ -0,0 +1 @@
i2pd --service=remove

6
i2p.cpp

@ -11,6 +11,8 @@
#include <signal.h> #include <signal.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#else
#include "./Win32/Win32Service.h"
#endif #endif
#include "Log.h" #include "Log.h"
@ -71,6 +73,9 @@ int main( int argc, char* argv[] )
setlocale(LC_ALL, "Russian"); setlocale(LC_ALL, "Russian");
#endif #endif
#ifdef _WIN32
service_control(isDaemon);
#endif
LogPrint("\n\n\n\ni2pd starting\n"); LogPrint("\n\n\n\ni2pd starting\n");
LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string()); LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string());
@ -168,6 +173,7 @@ int main( int argc, char* argv[] )
} }
LogPrint("Shutdown started."); LogPrint("Shutdown started.");
httpProxy.Stop ();
i2p::stream::StopStreaming (); i2p::stream::StopStreaming ();
i2p::garlic::routing.Stop (); i2p::garlic::routing.Stop ();
i2p::tunnel::tunnels.Stop (); i2p::tunnel::tunnels.Stop ();

Loading…
Cancel
Save