suricata
win32-service.c
Go to the documentation of this file.
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"
31 #include "util-debug.h"
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 {
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;
56 }
57 
58 /**
59  * \brief Detect if running as service or console app
60  */
61 static void SCAtExitHandler(void)
62 {
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("Can't set service status: %d", (int)GetLastError());
78  } else {
79  SCLogInfo("Service status set to: SERVICE_STOPPED");
80  }
81 }
82 
83 /**
84  * \brief Service handler
85  */
86 static DWORD WINAPI SCServiceCtrlHandlerEx(DWORD code, DWORD etype, LPVOID edata, LPVOID context)
87 {
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("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;
116 }
117 
118 /**
119  * \brief Service main function
120  */
121 static void WINAPI SCServiceMain(uint32_t argc, char** argv)
122 {
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 
133  if ((service_status_handle = RegisterServiceCtrlHandlerEx((char *)PROG_NAME, SCServiceCtrlHandlerEx, NULL)) == (SERVICE_STATUS_HANDLE)0) {
134  SCLogError("Can't register service control handler: %d", (int)GetLastError());
135  return;
136  }
137 
138  /* register exit handler */
139  if (atexit(SCAtExitHandler)) {
140  SCLogWarning("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("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("Can't set service status: %d", (int)GetLastError());
162  } else {
163  SCLogInfo("Service status set to: SERVICE_STOPPED");
164  }
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 {
175  SERVICE_TABLE_ENTRY DispatchTable[] = {
176  {(char *)PROG_NAME, (LPSERVICE_MAIN_FUNCTION) SCServiceMain},
177  {NULL, NULL}
178  };
179 
180  /* continue with suricata initialization */
181  if (service_initialized) {
182  SCLogWarning("Service is already initialized.");
183  return 0;
184  }
185 
186  /* save args */
187  service_argc = argc;
188  service_argv = argv;
189 
190  service_initialized = 1;
191 
192  SCLogInfo("Entering service control dispatcher...");
193 
194  if (!StartServiceCtrlDispatcher(DispatchTable)) {
195  /* exit with failure */
196  exit(EXIT_FAILURE);
197  }
198 
199  SCLogInfo("Leaving service control dispatcher.");
200 
201  /* exit with success */
202  exit(EXIT_SUCCESS);
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 {
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  path[0] = '"';
223  if (GetModuleFileName(NULL, path + 1, sizeof(path) - 1) == 0) {
224  SCLogError("Can't get path to service binary: %d", (int)GetLastError());
225  break;
226  }
227  if (strlcat(path, "\"", sizeof(path)) >= sizeof(path)) {
228  SCLogError("failed to construct service path string: path truncated: %s", path);
229  break;
230  }
231 
232  /* skip name of binary itself */
233  for (i = 1; i < argc; i++) {
234  if ((strlen(argv[i]) <= strlen("--service-install")) && (strncmp("--service-install", argv[i], strlen(argv[i])) == 0)) {
235  continue;
236  }
237  if (strlcat(path, " ", sizeof(path)) >= sizeof(path)) {
238  SCLogError("failed to construct service path string: path truncated: %s", path);
239  return -1;
240  }
241  if (strlcat(path, argv[i], sizeof(path)) >= sizeof(path)) {
242  SCLogError("failed to construct service path string: path truncated: %s", path);
243  return -1;
244  }
245  }
246 
247  if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) {
248  SCLogError("Can't open SCM: %d", (int)GetLastError());
249  break;
250  }
251 
252  service = CreateService(
253  scm,
254  PROG_NAME,
255  PROG_NAME,
256  SERVICE_ALL_ACCESS,
257  SERVICE_WIN32_OWN_PROCESS,
258  SERVICE_DEMAND_START,
259  SERVICE_ERROR_NORMAL,
260  path,
261  NULL,
262  NULL,
263  NULL,
264  NULL,
265  NULL);
266 
267  if (service == NULL) {
268  SCLogError("Can't create service: %d", (int)GetLastError());
269  break;
270  }
271 
272  ret = 0;
273 
274  } while(0);
275 
276  if (service) {
277  CloseServiceHandle(service);
278  }
279 
280  if (scm) {
281  CloseServiceHandle(scm);
282  }
283 
284  return ret;
285 }
286 
287 /**
288  * \brief Remove suricata service
289  *
290  * \param argc num of arguments
291  * \param argv passed arguments
292  */
293 int SCServiceRemove(void)
294 {
295  SERVICE_STATUS status;
296  SC_HANDLE service = NULL;
297  SC_HANDLE scm = NULL;
298  int ret = -1;
299 
300  do {
301  if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) {
302  SCLogError("Can't open SCM: %d", (int)GetLastError());
303  break;
304  }
305 
306  if ((service = OpenService(scm, PROG_NAME, SERVICE_ALL_ACCESS)) == NULL) {
307  SCLogError("Can't open service: %d", (int)GetLastError());
308  break;
309  }
310 
311  if (!QueryServiceStatus(service, &status)) {
312  SCLogError("Can't query service status: %d", (int)GetLastError());
313  break;
314  }
315 
316  if (status.dwCurrentState != SERVICE_STOPPED) {
317  SCLogError("Service isn't in stopped state: %d", (int)GetLastError());
318  break;
319  }
320 
321  if (!DeleteService(service)) {
322  SCLogError("Can't delete service: %d", (int)GetLastError());
323  break;
324  }
325 
326  ret = 0;
327 
328  } while(0);
329 
330  if (service) {
331  CloseServiceHandle(service);
332  }
333 
334  if (scm) {
335  CloseServiceHandle(scm);
336  }
337 
338  return ret;
339 }
340 
341 /**
342  * \brief Change suricata service startup parameters
343  *
344  * \param argc num of arguments
345  * \param argv passed arguments
346  */
347 int SCServiceChangeParams(int argc, char **argv)
348 {
349  char path[2048];
350  SC_HANDLE service = NULL;
351  SC_HANDLE scm = NULL;
352  int ret = -1;
353  int i = 0;
354 
355  do {
356  memset(path, 0, sizeof(path));
357 
358  path[0] = '"';
359  if (GetModuleFileName(NULL, path + 1, sizeof(path) - 1) == 0) {
360  SCLogError("Can't get path to service binary: %d", (int)GetLastError());
361  break;
362  }
363  if (strlcat(path, "\"", sizeof(path)) >= sizeof(path)) {
364  SCLogError("failed to construct service path string: path truncated: %s", path);
365  break;
366  }
367 
368  /* skip name of binary itself */
369  for (i = 1; i < argc; i++) {
370  if ((strlen(argv[i]) <= strlen("--service-change-params")) && (strncmp("--service-change-params", argv[i], strlen(argv[i])) == 0)) {
371  continue;
372  }
373  if (strlcat(path, " ", sizeof(path)) >= sizeof(path)) {
374  SCLogError("failed to construct service path string: path truncated: %s", path);
375  return -1;
376  }
377  if (strlcat(path, argv[i], sizeof(path)) >= sizeof(path)) {
378  SCLogError("failed to construct service path string: path truncated: %s", path);
379  return -1;
380  }
381  }
382 
383  if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) {
384  SCLogError("Can't open SCM: %d", (int)GetLastError());
385  break;
386  }
387 
388  if ((service = OpenService(scm, PROG_NAME, SERVICE_ALL_ACCESS)) == NULL) {
389  SCLogError("Can't open service: %d", (int)GetLastError());
390  break;
391  }
392 
393  if (!ChangeServiceConfig(
394  service,
395  SERVICE_WIN32_OWN_PROCESS,
396  SERVICE_DEMAND_START,
397  SERVICE_ERROR_NORMAL,
398  path,
399  NULL,
400  NULL,
401  NULL,
402  NULL,
403  NULL,
404  PROG_NAME))
405  {
406  SCLogError("Can't change service configuration: %d", (int)GetLastError());
407  break;
408  }
409 
410  ret = 0;
411 
412  } while(0);
413 
414  return ret;
415 }
416 
417 #endif /* OS_WIN32 */
util-debug.h
strlcat
size_t strlcat(char *, const char *src, size_t siz)
Definition: util-strlcatu.c:45
main
int main(int argc, char **argv)
Definition: main.c:20
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:262
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:232
PROG_NAME
#define PROG_NAME
Definition: suricata.h:75
suricata-common.h
EngineStop
void EngineStop(void)
make sure threads can stop the engine by calling this function. Purpose: pcap file mode needs to be a...
Definition: suricata.c:492
win32-service.h
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:274
suricata.h