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