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(SC_ERR_SVC, "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(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;
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(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  }
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(SC_ERR_SVC, "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  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;
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 {
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;
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 {
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;
393 }
394 
395 #endif /* OS_WIN32 */
main
int main(int argc, char **argv)
Definition: main.c:20
util-debug.h
strlcat
size_t strlcat(char *, const char *src, size_t siz)
Definition: util-strlcatu.c:45
SC_ERR_SVC
@ SC_ERR_SVC
Definition: util-error.h:194
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:217
PROG_NAME
#define PROG_NAME
Definition: suricata.h:71
suricata-common.h
SCLogError
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:257
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:391
win32-service.h
SCLogWarning
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:244
suricata.h
code
uint8_t code
Definition: decode-icmpv4.h:1