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 
32 static SERVICE_STATUS_HANDLE service_status_handle = 0;
33 
34 static int service_argc = 0;
35 
36 static char **service_argv = NULL;
37 
38 static int service_initialized = 0;
39 
40 int main(int argc, char **argv);
41 
42 /**
43  * \brief Detect if running as service or console app
44  */
45 int SCRunningAsService(void)
46 {
47  HANDLE h = INVALID_HANDLE_VALUE;
48  if ((h = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
49  SCLogInfo("Running as service: yes");
50  return 1;
51  }
52  CloseHandle(h);
53  SCLogInfo("Running as service: no");
54  return 0;
55 }
56 
57 /**
58  * \brief Detect if running as service or console app
59  */
60 static void SCAtExitHandler(void)
61 {
62  SERVICE_STATUS status = {
63  SERVICE_WIN32,
64  SERVICE_STOPPED,
65  SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN,
66  NO_ERROR,
67  NO_ERROR,
68  0,
69  0
70  };
71 
72  SCLogInfo("Exit handler called.");
73 
74  /* mark service as stopped */
75  if (!SetServiceStatus(service_status_handle, &status)) {
76  SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError());
77  } else {
78  SCLogInfo("Service status set to: SERVICE_STOPPED");
79  }
80 }
81 
82 /**
83  * \brief Service handler
84  */
85 static DWORD WINAPI SCServiceCtrlHandlerEx(DWORD code, DWORD etype, LPVOID edata, LPVOID context)
86 {
87  if (code == SERVICE_CONTROL_SHUTDOWN || code == SERVICE_CONTROL_STOP) {
88  SERVICE_STATUS status = {
89  SERVICE_WIN32,
90  SERVICE_STOP_PENDING,
91  SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN,
92  NO_ERROR,
93  NO_ERROR,
94  0,
95  0
96  };
97 
98  SCLogInfo("Service control handler called with %s control code.",
99  ((code == SERVICE_CONTROL_SHUTDOWN) ? ("SERVICE_CONTROL_SHUTDOWN") : ("SERVICE_CONTROL_STOP")));
100 
101  /* mark service as stop pending */
102  if (!SetServiceStatus(service_status_handle, &status)) {
103  SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError());
104  } else {
105  SCLogInfo("Service status set to: SERVICE_STOP_PENDING");
106  }
107 
108  /* mark engine as stopping */
109  EngineStop();
110 
111  return NO_ERROR;
112  }
113 
114  return ERROR_CALL_NOT_IMPLEMENTED;
115 }
116 
117 /**
118  * \brief Service main function
119  */
120 static void WINAPI SCServiceMain(uint32_t argc, char** argv)
121 {
122  SERVICE_STATUS status = {
123  SERVICE_WIN32,
124  SERVICE_RUNNING,
125  SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN,
126  NO_ERROR,
127  NO_ERROR,
128  0,
129  0
130  };
131 
132  if ((service_status_handle = RegisterServiceCtrlHandlerEx((char *)PROG_NAME, SCServiceCtrlHandlerEx, NULL)) == (SERVICE_STATUS_HANDLE)0) {
133  SCLogError(SC_ERR_SVC, "Can't register service control handler: %d", (int)GetLastError());
134  return;
135  }
136 
137  /* register exit handler */
138  if (atexit(SCAtExitHandler)) {
139  SCLogWarning(SC_ERR_SVC, "Can't register exit handler: %d", (int)GetLastError());
140  }
141 
142  /* mark service as running immediately */
143  if (!SetServiceStatus(service_status_handle, &status)) {
144  SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError());
145  } else {
146  SCLogInfo("Service status set to: SERVICE_RUNNING");
147  }
148 
149  SCLogInfo("Entering main function...");
150 
151  /* suricata initialization -> main loop -> uninitialization */
152  main(service_argc, service_argv);
153 
154  SCLogInfo("Leaving main function.");
155 
156  /* mark service as stopped */
157  status.dwCurrentState = SERVICE_STOPPED;
158 
159  if (!SetServiceStatus(service_status_handle, &status)) {
160  SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError());
161  } else {
162  SCLogInfo("Service status set to: SERVICE_STOPPED");
163  }
164 }
165 
166 /**
167  * \brief Init suricata service
168  *
169  * \param argc num of arguments
170  * \param argv passed arguments
171  */
172 int SCServiceInit(int argc, char **argv)
173 {
174  SERVICE_TABLE_ENTRY DispatchTable[] = {
175  {(char *)PROG_NAME, (LPSERVICE_MAIN_FUNCTION) SCServiceMain},
176  {NULL, NULL}
177  };
178 
179  /* continue with suricata initialization */
180  if (service_initialized) {
181  SCLogWarning(SC_ERR_SVC, "Service is already initialized.");
182  return 0;
183  }
184 
185  /* save args */
186  service_argc = argc;
187  service_argv = argv;
188 
189  service_initialized = 1;
190 
191  SCLogInfo("Entering service control dispatcher...");
192 
193  if (!StartServiceCtrlDispatcher(DispatchTable)) {
194  /* exit with failure */
195  exit(EXIT_FAILURE);
196  }
197 
198  SCLogInfo("Leaving service control dispatcher.");
199 
200  /* exit with success */
201  exit(EXIT_SUCCESS);
202 }
203 
204 /**
205  * \brief Install suricata as service
206  *
207  * \param argc num of arguments
208  * \param argv passed arguments
209  */
210 int SCServiceInstall(int argc, char **argv)
211 {
212  char path[2048];
213  SC_HANDLE service = NULL;
214  SC_HANDLE scm = NULL;
215  int ret = -1;
216  int i = 0;
217 
218  do {
219  memset(path, 0, sizeof(path));
220 
221  if (GetModuleFileName(NULL, path, MAX_PATH) == 0 ){
222  SCLogError(SC_ERR_SVC, "Can't get path to service binary: %d", (int)GetLastError());
223  break;
224  }
225 
226  /* skip name of binary itself */
227  for (i = 1; i < argc; i++) {
228  if ((strlen(argv[i]) <= strlen("--service-install")) && (strncmp("--service-install", argv[i], strlen(argv[i])) == 0)) {
229  continue;
230  }
231  strlcat(path, " ", sizeof(path) - strlen(path) - 1);
232  strlcat(path, argv[i], sizeof(path) - strlen(path) - 1);
233  }
234 
235  if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) {
236  SCLogError(SC_ERR_SVC, "Can't open SCM: %d", (int)GetLastError());
237  break;
238  }
239 
240  service = CreateService(
241  scm,
242  PROG_NAME,
243  PROG_NAME,
244  SERVICE_ALL_ACCESS,
245  SERVICE_WIN32_OWN_PROCESS,
246  SERVICE_DEMAND_START,
247  SERVICE_ERROR_NORMAL,
248  path,
249  NULL,
250  NULL,
251  NULL,
252  NULL,
253  NULL);
254 
255  if (service == NULL) {
256  SCLogError(SC_ERR_SVC, "Can't create service: %d", (int)GetLastError());
257  break;
258  }
259 
260  ret = 0;
261 
262  } while(0);
263 
264  if (service) {
265  CloseServiceHandle(service);
266  }
267 
268  if (scm) {
269  CloseServiceHandle(scm);
270  }
271 
272  return ret;
273 }
274 
275 /**
276  * \brief Remove suricata service
277  *
278  * \param argc num of arguments
279  * \param argv passed arguments
280  */
281 int SCServiceRemove(int argc, char **argv)
282 {
283  SERVICE_STATUS status;
284  SC_HANDLE service = NULL;
285  SC_HANDLE scm = NULL;
286  int ret = -1;
287 
288  do {
289  if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) {
290  SCLogError(SC_ERR_SVC, "Can't open SCM: %d", (int)GetLastError());
291  break;
292  }
293 
294  if ((service = OpenService(scm, PROG_NAME, SERVICE_ALL_ACCESS)) == NULL) {
295  SCLogError(SC_ERR_SVC, "Can't open service: %d", (int)GetLastError());
296  break;
297  }
298 
299  if (!QueryServiceStatus(service, &status)) {
300  SCLogError(SC_ERR_SVC, "Can't query service status: %d", (int)GetLastError());
301  break;
302  }
303 
304  if (status.dwCurrentState != SERVICE_STOPPED) {
305  SCLogError(SC_ERR_SVC, "Service isn't in stopped state: %d", (int)GetLastError());
306  break;
307  }
308 
309  if (!DeleteService(service)) {
310  SCLogError(SC_ERR_SVC, "Can't delete service: %d", (int)GetLastError());
311  break;
312  }
313 
314  ret = 0;
315 
316  } while(0);
317 
318  if (service) {
319  CloseServiceHandle(service);
320  }
321 
322  if (scm) {
323  CloseServiceHandle(scm);
324  }
325 
326  return ret;
327 }
328 
329 /**
330  * \brief Change suricata service startup parameters
331  *
332  * \param argc num of arguments
333  * \param argv passed arguments
334  */
335 int SCServiceChangeParams(int argc, char **argv)
336 {
337  char path[2048];
338  SC_HANDLE service = NULL;
339  SC_HANDLE scm = NULL;
340  int ret = -1;
341  int i = 0;
342 
343  do {
344  memset(path, 0, sizeof(path));
345 
346  if (GetModuleFileName(NULL, path, MAX_PATH) == 0 ){
347  SCLogError(SC_ERR_SVC, "Can't get path to service binary: %d", (int)GetLastError());
348  break;
349  }
350 
351  /* skip name of binary itself */
352  for (i = 1; i < argc; i++) {
353  if ((strlen(argv[i]) <= strlen("--service-change-params")) && (strncmp("--service-change-params", argv[i], strlen(argv[i])) == 0)) {
354  continue;
355  }
356  strlcat(path, " ", sizeof(path) - strlen(path) - 1);
357  strlcat(path, argv[i], sizeof(path) - strlen(path) - 1);
358  }
359 
360  if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) {
361  SCLogError(SC_ERR_SVC, "Can't open SCM: %d", (int)GetLastError());
362  break;
363  }
364 
365  if ((service = OpenService(scm, PROG_NAME, SERVICE_ALL_ACCESS)) == NULL) {
366  SCLogError(SC_ERR_SVC, "Can't open service: %d", (int)GetLastError());
367  break;
368  }
369 
370  if (!ChangeServiceConfig(
371  service,
372  SERVICE_WIN32_OWN_PROCESS,
373  SERVICE_DEMAND_START,
374  SERVICE_ERROR_NORMAL,
375  path,
376  NULL,
377  NULL,
378  NULL,
379  NULL,
380  NULL,
381  PROG_NAME))
382  {
383  SCLogError(SC_ERR_SVC, "Can't change service configuration: %d", (int)GetLastError());
384  break;
385  }
386 
387  ret = 0;
388 
389  } while(0);
390 
391  return ret;
392 }
393 
394 #endif /* OS_WIN32 */
#define PROG_NAME
Definition: suricata.h:71
size_t strlcat(char *, const char *src, size_t siz)
Definition: util-strlcatu.c:45
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:281
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:254
int main(int argc, char **argv)
Definition: suricata.c:2885
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:422
uint8_t code