]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - win/win32service.cpp
Fix a bunch of really obvious unnecessary includes.
[user/henk/code/inspircd.git] / win / win32service.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2013 Sadie Powell <sadie@witchery.services>
5  *   Copyright (C) 2012-2013 ChrisTX <xpipe@hotmail.de>
6  *   Copyright (C) 2012-2013 Attila Molnar <attilamolnar@hush.com>
7  *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
8  *   Copyright (C) 2008 Craig Edwards <brain@inspircd.org>
9  *
10  * This file is part of InspIRCd.  InspIRCd is free software: you can
11  * redistribute it and/or modify it under the terms of the GNU General Public
12  * License as published by the Free Software Foundation, version 2.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23
24 #include "inspircd.h"
25 #include "exitcodes.h"
26 #include <windows.h>
27 #include <cstdlib>
28 #include <cstring>
29 #include <cstdio>
30 #include <iostream>
31
32 static SERVICE_STATUS_HANDLE g_ServiceStatusHandle;
33 static SERVICE_STATUS g_ServiceStatus;
34 static bool g_bRunningAsService;
35
36 struct Service_Data {
37         DWORD argc;
38         LPSTR *argv;
39 };
40
41 static Service_Data g_ServiceData;
42
43 /** The main part of inspircd runs within this thread function. This allows the service part to run
44  * separately on its own and to be able to kill the worker thread when its time to quit.
45  */
46 DWORD WINAPI WorkerThread(LPVOID param)
47 {
48         smain(g_ServiceData.argc, g_ServiceData.argv);
49         return 0;
50 }
51
52 /* This is called when all startup is done */
53 void SetServiceRunning()
54 {
55         if (!g_bRunningAsService)
56                 return;
57
58         g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
59         g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
60
61         if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) )
62                 throw CWin32Exception();
63 }
64
65 /* In windows we hook this to InspIRCd::Exit() */
66 void SetServiceStopped(DWORD dwStatus)
67 {
68         if (!g_bRunningAsService)
69                 return;
70
71         g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
72         if(dwStatus != EXIT_STATUS_NOERROR)
73         {
74                 g_ServiceStatus.dwServiceSpecificExitCode = dwStatus;
75                 g_ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
76         }
77         else
78         {
79                 g_ServiceStatus.dwWin32ExitCode = ERROR_SUCCESS;
80         }
81         SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
82 }
83
84 /** This callback is called by windows when the state of the service has been changed */
85 VOID ServiceCtrlHandler(DWORD controlCode)
86 {
87         switch(controlCode)
88         {
89                 case SERVICE_CONTROL_SHUTDOWN:
90                 case SERVICE_CONTROL_STOP:
91                         g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
92                         SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
93                         break;
94         }
95 }
96
97 /** This callback is called by windows when the service is started */
98 VOID ServiceMain(DWORD argc, LPCSTR *argv)
99 {
100         g_ServiceStatusHandle = RegisterServiceCtrlHandler(TEXT("InspIRCd"), (LPHANDLER_FUNCTION)ServiceCtrlHandler);
101         if( !g_ServiceStatusHandle )
102                 return;
103
104         g_ServiceStatus.dwCheckPoint = 1;
105         g_ServiceStatus.dwControlsAccepted = 0;
106         g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
107         g_ServiceStatus.dwWaitHint = 5000;
108         g_ServiceStatus.dwWin32ExitCode = NO_ERROR;
109         g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
110
111         if( !SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus ) )
112                 return;
113
114         char szModuleName[MAX_PATH];
115         if(GetModuleFileNameA(NULL, szModuleName, MAX_PATH))
116         {
117                 if(!argc)
118                         argc = 1;
119
120                 g_ServiceData.argc = argc;
121
122                 // Note: since this memory is going to stay allocated for the rest of the execution,
123                 //               it doesn't make sense to free it, as it's going to be "freed" on process termination
124                 try {
125                         g_ServiceData.argv = new char*[argc];
126
127                         uint32_t allocsize = strnlen_s(szModuleName, MAX_PATH) + 1;
128                         g_ServiceData.argv[0] = new char[allocsize];
129                         strcpy_s(g_ServiceData.argv[0], allocsize, szModuleName);
130
131                         for(uint32_t i = 1; i < argc; i++)
132                         {
133                                 allocsize = strnlen_s(argv[i], MAX_PATH) + 1;
134                                 g_ServiceData.argv[i] = new char[allocsize];
135                                 strcpy_s(g_ServiceData.argv[i], allocsize, argv[i]);
136                         }
137
138                         *(strrchr(szModuleName, '\\') + 1) = NULL;
139                         SetCurrentDirectoryA(szModuleName);
140
141                         HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkerThread, NULL, 0, NULL);
142                         if (hThread != NULL)
143                         {
144                                 WaitForSingleObject(hThread, INFINITE);
145                                 CloseHandle(hThread);
146                         }
147                 }
148                 catch(...)
149                 {
150                         g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
151                         g_ServiceStatus.dwWin32ExitCode = ERROR_OUTOFMEMORY;
152                         SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
153                 }
154         }
155         if(g_ServiceStatus.dwCurrentState == SERVICE_STOPPED)
156                 return;
157
158         g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
159         g_ServiceStatus.dwWin32ExitCode = GetLastError();
160         SetServiceStatus( g_ServiceStatusHandle, &g_ServiceStatus );
161 }
162
163 /** Install the windows service. This requires administrator privileges. */
164 void InstallService()
165 {
166         SC_HANDLE InspServiceHandle = 0, SCMHandle = 0;
167
168         try {
169                 TCHAR tszBinaryPath[MAX_PATH];
170                 if(!GetModuleFileName(NULL, tszBinaryPath, _countof(tszBinaryPath)))
171                 {
172                         throw CWin32Exception();
173                 }
174
175                 SCMHandle = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
176                 if (!SCMHandle)
177                 {
178                         throw CWin32Exception();
179                 }
180
181                 InspServiceHandle = CreateService(SCMHandle, TEXT("InspIRCd"),TEXT("InspIRCd Daemon"), SERVICE_CHANGE_CONFIG, SERVICE_WIN32_OWN_PROCESS,
182                         SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, tszBinaryPath, 0, 0, 0, TEXT("NT AUTHORITY\\NetworkService"), NULL);
183
184                 if (!InspServiceHandle)
185                 {
186                         throw CWin32Exception();
187                 }
188
189                 TCHAR tszDescription[] = TEXT("The InspIRCd service hosts IRC channels and conversations. If this service is stopped, the IRC server will be unavailable.");
190                 SERVICE_DESCRIPTION svDescription = { tszDescription };
191                 if(!ChangeServiceConfig2(InspServiceHandle, SERVICE_CONFIG_DESCRIPTION, &svDescription))
192                 {
193                         throw CWin32Exception();
194                 }
195
196                 CloseServiceHandle(InspServiceHandle);
197                 CloseServiceHandle(SCMHandle);
198                 std::cout << "Service installed." << std::endl;
199         }
200         catch(CWin32Exception e)
201         {
202                 if(InspServiceHandle)
203                         CloseServiceHandle(InspServiceHandle);
204
205                 if(SCMHandle)
206                         CloseServiceHandle(SCMHandle);
207
208                 std::cout << "Service installation failed: " << e.what() << std::endl;
209         }
210 }
211
212 /** Remove the windows service. This requires administrator privileges. */
213 void UninstallService()
214 {
215         SC_HANDLE InspServiceHandle = 0, SCMHandle = 0;
216
217         try
218         {
219                 SCMHandle = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, DELETE);
220                 if (!SCMHandle)
221                         throw CWin32Exception();
222
223                 InspServiceHandle = OpenService(SCMHandle, TEXT("InspIRCd"), DELETE);
224                 if (!InspServiceHandle)
225                         throw CWin32Exception();
226
227                 if (!DeleteService(InspServiceHandle) && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
228                 {
229                         throw CWin32Exception();
230                 }
231
232                 CloseServiceHandle(InspServiceHandle);
233                 CloseServiceHandle(SCMHandle);
234                 std::cout << "Service removed." << std::endl;
235         }
236         catch(CWin32Exception e)
237         {
238                 if(InspServiceHandle)
239                         CloseServiceHandle(InspServiceHandle);
240
241                 if(SCMHandle)
242                         CloseServiceHandle(SCMHandle);
243
244                 std::cout << "Service deletion failed: " << e.what() << std::endl;
245         }
246 }
247
248 /* In windows, our main() flows through here, before calling the 'real' main, smain() in inspircd.cpp */
249 int main(int argc, char* argv[])
250 {
251         /* Check for parameters */
252         if (argc > 1)
253         {
254                 for (int i = 1; i < argc; i++)
255                 {
256                         if(!_stricmp(argv[i], "--installservice"))
257                         {
258                                 InstallService();
259                                 return 0;
260                         }
261                         if(!_stricmp(argv[i], "--uninstallservice") || !_stricmp(argv[i], "--removeservice"))
262                         {
263                                 UninstallService();
264                                 return 0;
265                         }
266                 }
267         }
268
269         SERVICE_TABLE_ENTRY serviceTable[] =
270         {
271                 { TEXT("InspIRCd"), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
272                 { NULL, NULL }
273         };
274
275         g_bRunningAsService = true;
276         if( !StartServiceCtrlDispatcher(serviceTable) )
277         {
278                 // This error means that the program was not started as service.
279                 if( GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT )
280                 {
281                         g_bRunningAsService = false;
282                         return smain(argc, argv);
283                 }
284                 else
285                 {
286                         return EXIT_STATUS_SERVICE;
287                 }
288         }
289         return 0;
290 }