]>
Commit | Line | Data |
---|---|---|
6bf7d760 OS |
1 | /* Copyright (C) 2007-2010 Open Information Security Foundation |
2 | * | |
3 | * You can copy, redistribute or modify this Program under the terms of | |
4 | * the GNU General Public License version 2 as published by the Free | |
5 | * Software Foundation. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | * | |
12 | * You should have received a copy of the GNU General Public License | |
13 | * version 2 along with this program; if not, write to the Free Software | |
14 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
15 | * 02110-1301, USA. | |
16 | */ | |
17 | ||
18 | /** | |
19 | * \file | |
20 | * | |
21 | * \author Ondrej Slanina <oslanina@kerio.com> | |
22 | * | |
23 | * Windows service functions | |
24 | */ | |
25 | ||
26 | #ifdef OS_WIN32 | |
27 | ||
28 | #include "suricata-common.h" | |
29 | #include "suricata.h" | |
30 | #include "win32-service.h" | |
d4613e5c | 31 | #include "util-debug.h" |
6bf7d760 OS |
32 | |
33 | static SERVICE_STATUS_HANDLE service_status_handle = 0; | |
34 | ||
35 | static int service_argc = 0; | |
36 | ||
37 | static char **service_argv = NULL; | |
38 | ||
39 | static int service_initialized = 0; | |
40 | ||
41 | int main(int argc, char **argv); | |
42 | ||
43 | /** | |
44 | * \brief Detect if running as service or console app | |
45 | */ | |
46 | int SCRunningAsService(void) | |
47 | { | |
9f135728 EL |
48 | HANDLE h = INVALID_HANDLE_VALUE; |
49 | if ((h = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) { | |
50 | SCLogInfo("Running as service: yes"); | |
51 | return 1; | |
52 | } | |
53 | CloseHandle(h); | |
54 | SCLogInfo("Running as service: no"); | |
55 | return 0; | |
6bf7d760 OS |
56 | } |
57 | ||
58 | /** | |
59 | * \brief Detect if running as service or console app | |
60 | */ | |
8c31cd4b | 61 | static void SCAtExitHandler(void) |
6bf7d760 | 62 | { |
9f135728 EL |
63 | SERVICE_STATUS status = { |
64 | SERVICE_WIN32, | |
65 | SERVICE_STOPPED, | |
66 | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN, | |
67 | NO_ERROR, | |
68 | NO_ERROR, | |
69 | 0, | |
70 | 0 | |
71 | }; | |
72 | ||
73 | SCLogInfo("Exit handler called."); | |
74 | ||
75 | /* mark service as stopped */ | |
76 | if (!SetServiceStatus(service_status_handle, &status)) { | |
77 | SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError()); | |
78 | } else { | |
79 | SCLogInfo("Service status set to: SERVICE_STOPPED"); | |
80 | } | |
6bf7d760 OS |
81 | } |
82 | ||
83 | /** | |
84 | * \brief Service handler | |
85 | */ | |
86 | static DWORD WINAPI SCServiceCtrlHandlerEx(DWORD code, DWORD etype, LPVOID edata, LPVOID context) | |
87 | { | |
9f135728 EL |
88 | if (code == SERVICE_CONTROL_SHUTDOWN || code == SERVICE_CONTROL_STOP) { |
89 | SERVICE_STATUS status = { | |
90 | SERVICE_WIN32, | |
91 | SERVICE_STOP_PENDING, | |
92 | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN, | |
93 | NO_ERROR, | |
94 | NO_ERROR, | |
95 | 0, | |
96 | 0 | |
97 | }; | |
98 | ||
99 | SCLogInfo("Service control handler called with %s control code.", | |
100 | ((code == SERVICE_CONTROL_SHUTDOWN) ? ("SERVICE_CONTROL_SHUTDOWN") : ("SERVICE_CONTROL_STOP"))); | |
101 | ||
102 | /* mark service as stop pending */ | |
103 | if (!SetServiceStatus(service_status_handle, &status)) { | |
104 | SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError()); | |
105 | } else { | |
106 | SCLogInfo("Service status set to: SERVICE_STOP_PENDING"); | |
107 | } | |
108 | ||
109 | /* mark engine as stopping */ | |
110 | EngineStop(); | |
111 | ||
112 | return NO_ERROR; | |
113 | } | |
114 | ||
115 | return ERROR_CALL_NOT_IMPLEMENTED; | |
6bf7d760 OS |
116 | } |
117 | ||
118 | /** | |
119 | * \brief Service main function | |
120 | */ | |
121 | static void WINAPI SCServiceMain(uint32_t argc, char** argv) | |
122 | { | |
9f135728 EL |
123 | SERVICE_STATUS status = { |
124 | SERVICE_WIN32, | |
125 | SERVICE_RUNNING, | |
126 | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN, | |
127 | NO_ERROR, | |
128 | NO_ERROR, | |
129 | 0, | |
130 | 0 | |
131 | }; | |
132 | ||
8c31cd4b | 133 | if ((service_status_handle = RegisterServiceCtrlHandlerEx((char *)PROG_NAME, SCServiceCtrlHandlerEx, NULL)) == (SERVICE_STATUS_HANDLE)0) { |
9f135728 EL |
134 | SCLogError(SC_ERR_SVC, "Can't register service control handler: %d", (int)GetLastError()); |
135 | return; | |
136 | } | |
137 | ||
138 | /* register exit handler */ | |
139 | if (atexit(SCAtExitHandler)) { | |
140 | SCLogWarning(SC_ERR_SVC, "Can't register exit handler: %d", (int)GetLastError()); | |
141 | } | |
142 | ||
143 | /* mark service as running immediately */ | |
144 | if (!SetServiceStatus(service_status_handle, &status)) { | |
145 | SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError()); | |
146 | } else { | |
147 | SCLogInfo("Service status set to: SERVICE_RUNNING"); | |
148 | } | |
149 | ||
150 | SCLogInfo("Entering main function..."); | |
151 | ||
152 | /* suricata initialization -> main loop -> uninitialization */ | |
153 | main(service_argc, service_argv); | |
154 | ||
155 | SCLogInfo("Leaving main function."); | |
156 | ||
157 | /* mark service as stopped */ | |
158 | status.dwCurrentState = SERVICE_STOPPED; | |
159 | ||
160 | if (!SetServiceStatus(service_status_handle, &status)) { | |
161 | SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError()); | |
162 | } else { | |
163 | SCLogInfo("Service status set to: SERVICE_STOPPED"); | |
164 | } | |
6bf7d760 OS |
165 | } |
166 | ||
167 | /** | |
168 | * \brief Init suricata service | |
169 | * | |
170 | * \param argc num of arguments | |
171 | * \param argv passed arguments | |
172 | */ | |
173 | int SCServiceInit(int argc, char **argv) | |
174 | { | |
9f135728 | 175 | SERVICE_TABLE_ENTRY DispatchTable[] = { |
223a38ae | 176 | {(char *)PROG_NAME, (LPSERVICE_MAIN_FUNCTION) SCServiceMain}, |
9f135728 EL |
177 | {NULL, NULL} |
178 | }; | |
6bf7d760 | 179 | |
9f135728 EL |
180 | /* continue with suricata initialization */ |
181 | if (service_initialized) { | |
182 | SCLogWarning(SC_ERR_SVC, "Service is already initialized."); | |
183 | return 0; | |
184 | } | |
6bf7d760 | 185 | |
9f135728 EL |
186 | /* save args */ |
187 | service_argc = argc; | |
188 | service_argv = argv; | |
6bf7d760 | 189 | |
9f135728 | 190 | service_initialized = 1; |
6bf7d760 | 191 | |
9f135728 | 192 | SCLogInfo("Entering service control dispatcher..."); |
6bf7d760 | 193 | |
9f135728 EL |
194 | if (!StartServiceCtrlDispatcher(DispatchTable)) { |
195 | /* exit with failure */ | |
196 | exit(EXIT_FAILURE); | |
197 | } | |
6bf7d760 | 198 | |
9f135728 | 199 | SCLogInfo("Leaving service control dispatcher."); |
6bf7d760 | 200 | |
9f135728 EL |
201 | /* exit with success */ |
202 | exit(EXIT_SUCCESS); | |
6bf7d760 OS |
203 | } |
204 | ||
205 | /** | |
206 | * \brief Install suricata as service | |
207 | * | |
208 | * \param argc num of arguments | |
209 | * \param argv passed arguments | |
210 | */ | |
211 | int SCServiceInstall(int argc, char **argv) | |
212 | { | |
9f135728 EL |
213 | char path[2048]; |
214 | SC_HANDLE service = NULL; | |
215 | SC_HANDLE scm = NULL; | |
216 | int ret = -1; | |
217 | int i = 0; | |
218 | ||
219 | do { | |
220 | memset(path, 0, sizeof(path)); | |
221 | ||
222 | if (GetModuleFileName(NULL, path, MAX_PATH) == 0 ){ | |
223 | SCLogError(SC_ERR_SVC, "Can't get path to service binary: %d", (int)GetLastError()); | |
224 | break; | |
225 | } | |
226 | ||
227 | /* skip name of binary itself */ | |
228 | for (i = 1; i < argc; i++) { | |
229 | if ((strlen(argv[i]) <= strlen("--service-install")) && (strncmp("--service-install", argv[i], strlen(argv[i])) == 0)) { | |
230 | continue; | |
231 | } | |
232 | strlcat(path, " ", sizeof(path) - strlen(path) - 1); | |
233 | strlcat(path, argv[i], sizeof(path) - strlen(path) - 1); | |
234 | } | |
235 | ||
236 | if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) { | |
237 | SCLogError(SC_ERR_SVC, "Can't open SCM: %d", (int)GetLastError()); | |
238 | break; | |
239 | } | |
240 | ||
241 | service = CreateService( | |
242 | scm, | |
243 | PROG_NAME, | |
244 | PROG_NAME, | |
245 | SERVICE_ALL_ACCESS, | |
246 | SERVICE_WIN32_OWN_PROCESS, | |
247 | SERVICE_DEMAND_START, | |
248 | SERVICE_ERROR_NORMAL, | |
249 | path, | |
250 | NULL, | |
251 | NULL, | |
252 | NULL, | |
253 | NULL, | |
254 | NULL); | |
255 | ||
256 | if (service == NULL) { | |
257 | SCLogError(SC_ERR_SVC, "Can't create service: %d", (int)GetLastError()); | |
258 | break; | |
259 | } | |
260 | ||
261 | ret = 0; | |
262 | ||
263 | } while(0); | |
264 | ||
265 | if (service) { | |
266 | CloseServiceHandle(service); | |
267 | } | |
268 | ||
269 | if (scm) { | |
270 | CloseServiceHandle(scm); | |
271 | } | |
272 | ||
273 | return ret; | |
6bf7d760 OS |
274 | } |
275 | ||
276 | /** | |
277 | * \brief Remove suricata service | |
278 | * | |
279 | * \param argc num of arguments | |
280 | * \param argv passed arguments | |
281 | */ | |
282 | int SCServiceRemove(int argc, char **argv) | |
283 | { | |
9f135728 EL |
284 | SERVICE_STATUS status; |
285 | SC_HANDLE service = NULL; | |
286 | SC_HANDLE scm = NULL; | |
287 | int ret = -1; | |
288 | ||
289 | do { | |
290 | if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) { | |
291 | SCLogError(SC_ERR_SVC, "Can't open SCM: %d", (int)GetLastError()); | |
292 | break; | |
293 | } | |
294 | ||
295 | if ((service = OpenService(scm, PROG_NAME, SERVICE_ALL_ACCESS)) == NULL) { | |
296 | SCLogError(SC_ERR_SVC, "Can't open service: %d", (int)GetLastError()); | |
297 | break; | |
298 | } | |
299 | ||
300 | if (!QueryServiceStatus(service, &status)) { | |
301 | SCLogError(SC_ERR_SVC, "Can't query service status: %d", (int)GetLastError()); | |
302 | break; | |
303 | } | |
304 | ||
305 | if (status.dwCurrentState != SERVICE_STOPPED) { | |
306 | SCLogError(SC_ERR_SVC, "Service isn't in stopped state: %d", (int)GetLastError()); | |
307 | break; | |
308 | } | |
309 | ||
310 | if (!DeleteService(service)) { | |
311 | SCLogError(SC_ERR_SVC, "Can't delete service: %d", (int)GetLastError()); | |
312 | break; | |
313 | } | |
314 | ||
315 | ret = 0; | |
316 | ||
317 | } while(0); | |
318 | ||
319 | if (service) { | |
320 | CloseServiceHandle(service); | |
321 | } | |
322 | ||
323 | if (scm) { | |
324 | CloseServiceHandle(scm); | |
325 | } | |
326 | ||
327 | return ret; | |
6bf7d760 OS |
328 | } |
329 | ||
330 | /** | |
331 | * \brief Change suricata service startup parameters | |
332 | * | |
333 | * \param argc num of arguments | |
334 | * \param argv passed arguments | |
335 | */ | |
336 | int SCServiceChangeParams(int argc, char **argv) | |
337 | { | |
9f135728 EL |
338 | char path[2048]; |
339 | SC_HANDLE service = NULL; | |
340 | SC_HANDLE scm = NULL; | |
341 | int ret = -1; | |
342 | int i = 0; | |
343 | ||
344 | do { | |
345 | memset(path, 0, sizeof(path)); | |
346 | ||
347 | if (GetModuleFileName(NULL, path, MAX_PATH) == 0 ){ | |
348 | SCLogError(SC_ERR_SVC, "Can't get path to service binary: %d", (int)GetLastError()); | |
349 | break; | |
350 | } | |
351 | ||
352 | /* skip name of binary itself */ | |
353 | for (i = 1; i < argc; i++) { | |
354 | if ((strlen(argv[i]) <= strlen("--service-change-params")) && (strncmp("--service-change-params", argv[i], strlen(argv[i])) == 0)) { | |
355 | continue; | |
356 | } | |
357 | strlcat(path, " ", sizeof(path) - strlen(path) - 1); | |
358 | strlcat(path, argv[i], sizeof(path) - strlen(path) - 1); | |
359 | } | |
360 | ||
361 | if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) { | |
362 | SCLogError(SC_ERR_SVC, "Can't open SCM: %d", (int)GetLastError()); | |
363 | break; | |
364 | } | |
365 | ||
366 | if ((service = OpenService(scm, PROG_NAME, SERVICE_ALL_ACCESS)) == NULL) { | |
367 | SCLogError(SC_ERR_SVC, "Can't open service: %d", (int)GetLastError()); | |
368 | break; | |
369 | } | |
370 | ||
371 | if (!ChangeServiceConfig( | |
372 | service, | |
373 | SERVICE_WIN32_OWN_PROCESS, | |
374 | SERVICE_DEMAND_START, | |
375 | SERVICE_ERROR_NORMAL, | |
376 | path, | |
377 | NULL, | |
378 | NULL, | |
379 | NULL, | |
380 | NULL, | |
381 | NULL, | |
382 | PROG_NAME)) | |
383 | { | |
384 | SCLogError(SC_ERR_SVC, "Can't change service configuration: %d", (int)GetLastError()); | |
385 | break; | |
386 | } | |
387 | ||
388 | ret = 0; | |
389 | ||
390 | } while(0); | |
391 | ||
392 | return ret; | |
6bf7d760 OS |
393 | } |
394 | ||
395 | #endif /* OS_WIN32 */ |