2012-04-19 20:58:29 +02:00
/*
* InspIRCd - - Internet Relay Chat Daemon
2008-08-21 11:01:51 +00:00
*
2024-06-14 09:13:03 +01:00
* Copyright ( C ) 2022 - 2024 Sadie Powell < sadie @ witchery . services >
2021-08-27 09:38:22 +01:00
* Copyright ( C ) 2021 Herman < GermanAizek @ yandex . ru >
2024-11-02 08:42:20 +00:00
* Copyright ( C ) 2013 ChrisTX < xpipe @ hotmail . de >
2020-01-11 22:02:47 +00:00
* Copyright ( C ) 2012 Robby < robby @ chatbelgie . be >
* Copyright ( C ) 2008 Craig Edwards < brain @ inspircd . org >
2008-08-21 11:01:51 +00:00
*
2012-04-19 20:58:29 +02:00
* This file is part of InspIRCd . InspIRCd 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.
2008-08-21 11:01:51 +00:00
*
2012-04-19 20:58:29 +02:00
* 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 , see < http : //www.gnu.org/licenses/>.
2008-08-22 20:01:13 +00:00
*/
2012-04-19 20:58:29 +02:00
2008-08-22 20:01:13 +00:00
# include "inspircd.h"
2022-01-26 08:50:29 +00:00
# include <windows.h>
2013-03-23 23:52:51 +01:00
static SERVICE_STATUS_HANDLE g_ServiceStatusHandle ;
static SERVICE_STATUS g_ServiceStatus ;
static bool g_bRunningAsService ;
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
struct Service_Data {
DWORD argc ;
2022-09-29 12:01:29 +01:00
LPSTR * argv ;
2008-08-22 20:01:13 +00:00
} ;
2013-03-23 23:52:51 +01:00
static Service_Data g_ServiceData ;
2008-08-22 20:01:13 +00:00
/** The main part of inspircd runs within this thread function. This allows the service part to run
2020-04-21 06:34:17 +00:00
* separately on its own and to be able to kill the worker thread when its time to quit .
2008-08-22 20:01:13 +00:00
*/
2013-03-23 23:52:51 +01:00
DWORD WINAPI WorkerThread ( LPVOID param )
2008-08-22 20:01:13 +00:00
{
2013-03-23 23:52:51 +01:00
smain ( g_ServiceData . argc , g_ServiceData . argv ) ;
2008-08-22 20:01:13 +00:00
return 0 ;
}
2008-08-24 19:08:36 +00:00
/* This is called when all startup is done */
void SetServiceRunning ( )
{
2013-03-23 23:52:51 +01:00
if ( ! g_bRunningAsService )
2008-08-24 19:08:36 +00:00
return ;
2013-03-23 23:52:51 +01:00
g_ServiceStatus . dwCurrentState = SERVICE_RUNNING ;
g_ServiceStatus . dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN ;
2008-08-24 19:08:36 +00:00
2013-03-23 23:52:51 +01:00
if ( ! SetServiceStatus ( g_ServiceStatusHandle , & g_ServiceStatus ) )
throw CWin32Exception ( ) ;
2008-08-22 20:01:13 +00:00
}
2023-07-13 13:31:06 +01:00
/* In windows we hook this to InspIRCd::Exit(EXIT_FAILURE) */
2013-03-23 23:52:51 +01:00
void SetServiceStopped ( DWORD dwStatus )
2008-08-22 20:01:13 +00:00
{
2013-03-23 23:52:51 +01:00
if ( ! g_bRunningAsService )
return ;
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
g_ServiceStatus . dwCurrentState = SERVICE_STOPPED ;
2023-07-13 13:31:06 +01:00
if ( dwStatus ! = EXIT_SUCCESS )
2008-08-22 20:01:13 +00:00
{
2013-03-23 23:52:51 +01:00
g_ServiceStatus . dwServiceSpecificExitCode = dwStatus ;
g_ServiceStatus . dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR ;
2008-08-22 20:01:13 +00:00
}
else
{
2013-03-23 23:52:51 +01:00
g_ServiceStatus . dwWin32ExitCode = ERROR_SUCCESS ;
2008-08-22 20:01:13 +00:00
}
2013-03-23 23:52:51 +01:00
SetServiceStatus ( g_ServiceStatusHandle , & g_ServiceStatus ) ;
2008-08-24 19:08:36 +00:00
}
2008-08-22 20:01:13 +00:00
/** This callback is called by windows when the state of the service has been changed */
2008-08-24 23:05:51 +00:00
VOID ServiceCtrlHandler ( DWORD controlCode )
2008-08-22 20:01:13 +00:00
{
switch ( controlCode )
{
case SERVICE_CONTROL_SHUTDOWN :
case SERVICE_CONTROL_STOP :
2013-03-23 23:52:51 +01:00
g_ServiceStatus . dwCurrentState = SERVICE_STOPPED ;
SetServiceStatus ( g_ServiceStatusHandle , & g_ServiceStatus ) ;
break ;
2008-08-22 20:01:13 +00:00
}
}
/** This callback is called by windows when the service is started */
2022-09-29 12:01:29 +01:00
VOID ServiceMain ( DWORD argc , LPCSTR * argv )
2008-08-22 20:01:13 +00:00
{
2013-03-23 23:52:51 +01:00
g_ServiceStatusHandle = RegisterServiceCtrlHandler ( TEXT ( " InspIRCd " ) , ( LPHANDLER_FUNCTION ) ServiceCtrlHandler ) ;
if ( ! g_ServiceStatusHandle )
return ;
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
g_ServiceStatus . dwCheckPoint = 1 ;
g_ServiceStatus . dwControlsAccepted = 0 ;
g_ServiceStatus . dwServiceType = SERVICE_WIN32_OWN_PROCESS ;
g_ServiceStatus . dwWaitHint = 5000 ;
g_ServiceStatus . dwWin32ExitCode = NO_ERROR ;
g_ServiceStatus . dwCurrentState = SERVICE_START_PENDING ;
if ( ! SetServiceStatus ( g_ServiceStatusHandle , & g_ServiceStatus ) )
2008-08-22 20:01:13 +00:00
return ;
2013-03-23 23:52:51 +01:00
char szModuleName [ MAX_PATH ] ;
2022-07-22 18:33:38 +01:00
if ( GetModuleFileNameA ( nullptr , szModuleName , MAX_PATH ) )
2008-08-22 20:01:13 +00:00
{
2013-03-23 23:52:51 +01:00
if ( ! argc )
argc = 1 ;
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
g_ServiceData . argc = argc ;
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
// Note: since this memory is going to stay allocated for the rest of the execution,
2021-04-27 16:36:40 +01:00
// it doesn't make sense to free it, as it's going to be "freed" on process termination
2013-03-23 23:52:51 +01:00
try {
g_ServiceData . argv = new char * [ argc ] ;
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
uint32_t allocsize = strnlen_s ( szModuleName , MAX_PATH ) + 1 ;
g_ServiceData . argv [ 0 ] = new char [ allocsize ] ;
strcpy_s ( g_ServiceData . argv [ 0 ] , allocsize , szModuleName ) ;
for ( uint32_t i = 1 ; i < argc ; i + + )
{
allocsize = strnlen_s ( argv [ i ] , MAX_PATH ) + 1 ;
g_ServiceData . argv [ i ] = new char [ allocsize ] ;
strcpy_s ( g_ServiceData . argv [ i ] , allocsize , argv [ i ] ) ;
}
2022-07-22 18:33:38 +01:00
* ( strrchr ( szModuleName , ' \\ ' ) + 1 ) = ' \0 ' ;
2013-03-23 23:52:51 +01:00
SetCurrentDirectoryA ( szModuleName ) ;
2022-07-22 18:33:38 +01:00
HANDLE hThread = CreateThread ( nullptr , 0 , ( LPTHREAD_START_ROUTINE ) WorkerThread , nullptr , 0 , nullptr ) ;
if ( hThread ! = nullptr )
2013-03-23 23:52:51 +01:00
{
WaitForSingleObject ( hThread , INFINITE ) ;
CloseHandle ( hThread ) ;
}
}
catch ( . . . )
{
g_ServiceStatus . dwCurrentState = SERVICE_STOPPED ;
g_ServiceStatus . dwWin32ExitCode = ERROR_OUTOFMEMORY ;
SetServiceStatus ( g_ServiceStatusHandle , & g_ServiceStatus ) ;
}
2008-08-22 20:01:13 +00:00
}
2013-03-23 23:52:51 +01:00
if ( g_ServiceStatus . dwCurrentState = = SERVICE_STOPPED )
return ;
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
g_ServiceStatus . dwCurrentState = SERVICE_STOPPED ;
g_ServiceStatus . dwWin32ExitCode = GetLastError ( ) ;
SetServiceStatus ( g_ServiceStatusHandle , & g_ServiceStatus ) ;
2008-08-22 20:01:13 +00:00
}
/** Install the windows service. This requires administrator privileges. */
void InstallService ( )
{
2013-03-23 23:52:51 +01:00
SC_HANDLE InspServiceHandle = 0 , SCMHandle = 0 ;
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
try {
TCHAR tszBinaryPath [ MAX_PATH ] ;
2022-07-22 18:33:38 +01:00
if ( ! GetModuleFileName ( nullptr , tszBinaryPath , _countof ( tszBinaryPath ) ) )
2013-03-23 23:52:51 +01:00
{
throw CWin32Exception ( ) ;
}
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
SCMHandle = OpenSCManager ( 0 , 0 , SC_MANAGER_ALL_ACCESS ) ;
if ( ! SCMHandle )
{
throw CWin32Exception ( ) ;
}
2008-08-22 20:01:13 +00:00
2022-12-01 05:14:58 +00:00
InspServiceHandle = CreateService ( SCMHandle , TEXT ( " InspIRCd " ) , TEXT ( " InspIRCd Daemon " ) , SERVICE_CHANGE_CONFIG , SERVICE_WIN32_OWN_PROCESS ,
2022-07-22 18:33:38 +01:00
SERVICE_AUTO_START , SERVICE_ERROR_NORMAL , tszBinaryPath , 0 , 0 , 0 , TEXT ( " NT AUTHORITY \\ NetworkService " ) , nullptr ) ;
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
if ( ! InspServiceHandle )
{
throw CWin32Exception ( ) ;
}
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
TCHAR tszDescription [ ] = TEXT ( " The InspIRCd service hosts IRC channels and conversations. If this service is stopped, the IRC server will be unavailable. " ) ;
SERVICE_DESCRIPTION svDescription = { tszDescription } ;
if ( ! ChangeServiceConfig2 ( InspServiceHandle , SERVICE_CONFIG_DESCRIPTION , & svDescription ) )
2008-08-22 20:01:13 +00:00
{
2013-03-23 23:52:51 +01:00
throw CWin32Exception ( ) ;
2008-08-22 20:01:13 +00:00
}
2013-03-23 23:52:51 +01:00
CloseServiceHandle ( InspServiceHandle ) ;
CloseServiceHandle ( SCMHandle ) ;
2024-09-16 09:54:27 +01:00
fmt : : println ( " Service installed. " ) ;
2008-08-22 20:01:13 +00:00
}
2021-06-12 07:59:13 +03:00
catch ( const CWin32Exception & e )
2013-03-23 23:52:51 +01:00
{
if ( InspServiceHandle )
CloseServiceHandle ( InspServiceHandle ) ;
if ( SCMHandle )
CloseServiceHandle ( SCMHandle ) ;
2008-08-22 20:01:13 +00:00
2024-09-16 09:54:27 +01:00
fmt : : println ( " Service installation failed: {} " , e . what ( ) ) ;
2013-03-23 23:52:51 +01:00
}
2008-08-22 20:01:13 +00:00
}
/** Remove the windows service. This requires administrator privileges. */
2013-03-23 23:52:51 +01:00
void UninstallService ( )
2008-08-22 20:01:13 +00:00
{
2013-03-23 23:52:51 +01:00
SC_HANDLE InspServiceHandle = 0 , SCMHandle = 0 ;
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
try
2008-08-22 20:01:13 +00:00
{
2022-07-22 18:33:38 +01:00
SCMHandle = OpenSCManager ( nullptr , SERVICES_ACTIVE_DATABASE , DELETE ) ;
2013-03-23 23:52:51 +01:00
if ( ! SCMHandle )
throw CWin32Exception ( ) ;
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
InspServiceHandle = OpenService ( SCMHandle , TEXT ( " InspIRCd " ) , DELETE ) ;
if ( ! InspServiceHandle )
throw CWin32Exception ( ) ;
2008-08-22 20:01:13 +00:00
2013-03-23 23:52:51 +01:00
if ( ! DeleteService ( InspServiceHandle ) & & GetLastError ( ) ! = ERROR_SERVICE_MARKED_FOR_DELETE )
{
throw CWin32Exception ( ) ;
}
CloseServiceHandle ( InspServiceHandle ) ;
CloseServiceHandle ( SCMHandle ) ;
2024-09-16 09:54:27 +01:00
fmt : : println ( " Service removed. " ) ;
2008-08-22 20:01:13 +00:00
}
2021-06-12 07:59:13 +03:00
catch ( const CWin32Exception & e )
2013-03-23 23:52:51 +01:00
{
if ( InspServiceHandle )
CloseServiceHandle ( InspServiceHandle ) ;
if ( SCMHandle )
CloseServiceHandle ( SCMHandle ) ;
2008-08-22 20:01:13 +00:00
2024-09-16 09:54:27 +01:00
fmt : : println ( " Service deletion failed: {} " , e . what ( ) ) ;
2013-03-23 23:52:51 +01:00
}
2008-08-22 20:01:13 +00:00
}
/* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */
2013-03-23 23:52:51 +01:00
int main ( int argc , char * argv [ ] )
2008-08-22 20:01:13 +00:00
{
/* Check for parameters */
if ( argc > 1 )
{
2013-03-23 23:52:51 +01:00
for ( int i = 1 ; i < argc ; i + + )
2008-08-22 20:01:13 +00:00
{
2013-03-23 23:52:51 +01:00
if ( ! _stricmp ( argv [ i ] , " --installservice " ) )
{
InstallService ( ) ;
2023-07-13 13:31:06 +01:00
return EXIT_SUCCESS ;
2013-03-23 23:52:51 +01:00
}
if ( ! _stricmp ( argv [ i ] , " --uninstallservice " ) | | ! _stricmp ( argv [ i ] , " --removeservice " ) )
2008-08-22 20:01:13 +00:00
{
2013-03-23 23:52:51 +01:00
UninstallService ( ) ;
2023-07-13 13:31:06 +01:00
return EXIT_SUCCESS ;
2008-08-22 20:01:13 +00:00
}
}
}
2013-03-23 23:52:51 +01:00
SERVICE_TABLE_ENTRY serviceTable [ ] =
2008-08-22 20:01:13 +00:00
{
2024-06-07 14:40:41 +01:00
{ ( LPSTR ) " InspIRCd " , ( LPSERVICE_MAIN_FUNCTION ) ServiceMain } ,
2022-07-22 18:33:38 +01:00
{ nullptr , nullptr }
2013-03-23 23:52:51 +01:00
} ;
g_bRunningAsService = true ;
if ( ! StartServiceCtrlDispatcher ( serviceTable ) )
{
// This error means that the program was not started as service.
if ( GetLastError ( ) = = ERROR_FAILED_SERVICE_CONTROLLER_CONNECT )
2008-08-22 20:01:13 +00:00
{
2013-03-23 23:52:51 +01:00
g_bRunningAsService = false ;
2008-08-22 20:01:13 +00:00
return smain ( argc , argv ) ;
}
2013-03-23 23:52:51 +01:00
else
{
2023-07-13 13:31:06 +01:00
return EXIT_FAILURE ;
2013-03-23 23:52:51 +01:00
}
2008-08-22 20:01:13 +00:00
}
2023-07-13 13:31:06 +01:00
return EXIT_SUCCESS ;
2008-08-22 20:01:13 +00:00
}