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