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