]> git.proxmox.com Git - mirror_ovs.git/blob - lib/daemon-windows.c
Use xstrdup() instead of strdup(), xmalloc() instead of malloc().
[mirror_ovs.git] / lib / daemon-windows.c
1 /*
2 * Copyright (c) 2014 Nicira, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <config.h>
18 #include "daemon.h"
19 #include "daemon-private.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include "poll-loop.h"
23 #include "vlog.h"
24
25 VLOG_DEFINE_THIS_MODULE(daemon_windows);
26
27 static bool service_create; /* Was --service specified? */
28 static bool service_started; /* Have we dispatched service to start? */
29
30 /* --service-monitor: Should the service be restarted if it dies
31 * unexpectedly? */
32 static bool monitor;
33
34 bool detach; /* Was --detach specified? */
35 static bool detached; /* Running as the child process. */
36 static HANDLE write_handle; /* End of pipe to write to parent. */
37
38 char *pidfile; /* --pidfile: Name of pidfile (null if none). */
39 static FILE *filep_pidfile; /* File pointer to access the pidfile. */
40
41 /* Handle to the Services Manager and the created service. */
42 static SC_HANDLE manager, service;
43
44 /* Handle to the status information structure for the current service. */
45 static SERVICE_STATUS_HANDLE hstatus;
46
47 /* Hold the service's current status. */
48 static SERVICE_STATUS service_status;
49
50 /* Handle to an event object used to wakeup from poll_block(). */
51 static HANDLE wevent;
52
53 /* Hold the arguments sent to the main function. */
54 static int sargc;
55 static char ***sargvp;
56
57 static void check_service(void);
58 static void handle_scm_callback(void);
59 static void init_service_status(void);
60 static void set_config_failure_actions(void);
61
62 static bool detach_process(int argc, char *argv[]);
63
64 extern int main(int argc, char *argv[]);
65
66 void
67 daemon_usage(void)
68 {
69 printf(
70 "\nService options:\n"
71 " --service run in background as a service.\n"
72 " --service-monitor restart the service in case of an "
73 "unexpected failure. \n",
74 ovs_rundir(), program_name);
75 }
76
77 /* Registers the call-back and configures the actions in case of a failure
78 * with the Windows services manager. */
79 void
80 service_start(int *argcp, char **argvp[])
81 {
82 int argc = *argcp;
83 char **argv = *argvp;
84 int i;
85 SERVICE_TABLE_ENTRY service_table[] = {
86 {(LPTSTR)program_name, (LPSERVICE_MAIN_FUNCTION)main},
87 {NULL, NULL}
88 };
89
90 /* If one of the command line option is "--detach", we create
91 * a new process in case of parent, wait for child to start and exit.
92 * In case of the child, we just return. We should not be creating a
93 * service in either case. */
94 if (detach_process(argc, argv)) {
95 return;
96 }
97
98 /* 'service_started' is 'false' when service_start() is called the first
99 * time. It is 'true', when it is called the second time by the Windows
100 * services manager. */
101 if (service_started) {
102 init_service_status();
103
104 wevent = CreateEvent(NULL, TRUE, FALSE, NULL);
105 if (!wevent) {
106 char *msg_buf = ovs_lasterror_to_string();
107 VLOG_FATAL("Failed to create a event (%s).", msg_buf);
108 }
109
110 poll_wevent_wait(wevent);
111
112 /* Register the control handler. This function is called by the service
113 * manager to stop the service. */
114 hstatus = RegisterServiceCtrlHandler(program_name,
115 (LPHANDLER_FUNCTION)control_handler);
116 if (!hstatus) {
117 char *msg_buf = ovs_lasterror_to_string();
118 VLOG_FATAL("Failed to register the service control handler (%s).",
119 msg_buf);
120 }
121
122 if (monitor) {
123 set_config_failure_actions();
124 }
125
126 /* When the service control manager does the call back, it does not
127 * send the same arguments as sent to the main function during the
128 * service start. So, use the arguments passed over during the first
129 * time. */
130 *argcp = sargc;
131 *argvp = *sargvp;
132
133 /* XXX: Windows implementation cannot have a unixctl commands in the
134 * traditional sense of unix domain sockets. If an implementation is
135 * done that involves 'unixctl' vlog commands the following call is
136 * needed to make sure that the unixctl commands for vlog get
137 * registered in a daemon, even before the first log message. */
138 vlog_init();
139
140 return;
141 }
142
143 assert_single_threaded();
144
145 /* A reference to arguments passed to the main function the first time.
146 * We need it after the call-back from service control manager. */
147 sargc = argc;
148 sargvp = argvp;
149
150 /* We are only interested in the '--service' and '--service-monitor'
151 * options before the call-back from the service control manager. */
152 for (i = 0; i < argc; i ++) {
153 if (!strcmp(argv[i], "--service")) {
154 service_create = true;
155 } else if (!strcmp(argv[i], "--service-monitor")) {
156 monitor = true;
157 }
158 }
159
160 /* If '--service' is not a command line option, run in foreground. */
161 if (!service_create) {
162 return;
163 }
164
165 /* If we have been configured to run as a service, then that service
166 * should already have been created either manually or through a start up
167 * script. */
168 check_service();
169
170 service_started = true;
171
172 /* StartServiceCtrlDispatcher blocks and returns after the service is
173 * stopped. */
174 if (!StartServiceCtrlDispatcher(service_table)) {
175 char *msg_buf = ovs_lasterror_to_string();
176 VLOG_FATAL("Failed at StartServiceCtrlDispatcher (%s)", msg_buf);
177 }
178 exit(0);
179 }
180
181 /* This function is registered with the Windows services manager through
182 * a call to RegisterServiceCtrlHandler() and will be called by the Windows
183 * services manager asynchronously to stop the service. */
184 void
185 control_handler(DWORD request)
186 {
187 switch (request) {
188 case SERVICE_CONTROL_STOP:
189 case SERVICE_CONTROL_SHUTDOWN:
190 service_status.dwCurrentState = SERVICE_STOPPED;
191 service_status.dwWin32ExitCode = NO_ERROR;
192 SetEvent(wevent);
193 break;
194
195 default:
196 break;
197 }
198 }
199
200 /* Return 'true' if the Windows services manager has called the
201 * control_handler() and asked the program to terminate. */
202 bool
203 should_service_stop(void)
204 {
205 if (service_started) {
206 if (service_status.dwCurrentState != SERVICE_RUNNING) {
207 return true;
208 } else {
209 poll_wevent_wait(wevent);
210 }
211 }
212 return false;
213 }
214
215 /* Set the service as stopped. The control manager will terminate the
216 * service soon after this call. Hence, this should ideally be the last
217 * call before termination. */
218 void
219 service_stop()
220 {
221 if (!service_started) {
222 return;
223 }
224 fatal_signal_atexit_handler();
225
226 ResetEvent(wevent);
227 CloseHandle(wevent);
228
229 service_status.dwCurrentState = SERVICE_STOPPED;
230 service_status.dwWin32ExitCode = NO_ERROR;
231 SetServiceStatus(hstatus, &service_status);
232 }
233
234 /* Call this function to signal that the daemon is ready. init_service()
235 * or control_handler() has already initalized/set the
236 * service_status.dwCurrentState .*/
237 static void
238 service_complete(void)
239 {
240 if (hstatus) {
241 SetServiceStatus(hstatus, &service_status);
242 }
243 }
244
245 /* Check whether 'program_name' has been created as a service. */
246 static void
247 check_service()
248 {
249 /* Establish a connection to the local service control manager. */
250 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
251 if (!manager) {
252 char *msg_buf = ovs_lasterror_to_string();
253 VLOG_FATAL("Failed to open the service control manager (%s).",
254 msg_buf);
255 }
256
257 service = OpenService(manager, program_name, SERVICE_ALL_ACCESS);
258 if (!service) {
259 char *msg_buf = ovs_lasterror_to_string();
260 VLOG_FATAL("Failed to open service (%s).", msg_buf);
261 }
262 }
263
264 /* Service status of a service can be checked asynchronously through
265 * tools like 'sc' or through Windows services manager and is set
266 * through a call to SetServiceStatus(). */
267 static void
268 init_service_status()
269 {
270 /* The service runs in its own process. */
271 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
272
273 /* The control codes the service accepts. */
274 service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
275 SERVICE_ACCEPT_SHUTDOWN;
276
277 /* Initialize the current state as SERVICE_RUNNING. */
278 service_status.dwCurrentState = SERVICE_RUNNING;
279
280 /* The exit code to indicate if there was an error. */
281 service_status.dwWin32ExitCode = NO_ERROR;
282
283 /* The checkpoint value the service increments periodically. Set as 0
284 * as we do not plan to periodically increment the value. */
285 service_status.dwCheckPoint = 0;
286
287 /* The estimated time required for the stop operation in ms. */
288 service_status.dwWaitHint = 1000;
289 }
290
291 /* In case of an unexpected termination, configure the action to be
292 * taken. */
293 static void
294 set_config_failure_actions()
295 {
296 /* In case of a failure, restart the process the first two times
297 * After 'dwResetPeriod', the failure count is reset. */
298 SC_ACTION fail_action[3] = {
299 {SC_ACTION_RESTART, 0},
300 {SC_ACTION_RESTART, 0},
301 {SC_ACTION_NONE, 0}
302 };
303 SERVICE_FAILURE_ACTIONS service_fail_action;
304
305 /* Reset failure count after (in seconds). */
306 service_fail_action.dwResetPeriod = 10;
307
308 /* Reboot message. */
309 service_fail_action.lpRebootMsg = NULL;
310
311 /* The command line of the process. */
312 service_fail_action.lpCommand = NULL;
313
314 /* Number of elements in 'fail_actions'. */
315 service_fail_action.cActions = sizeof(fail_action)/sizeof(fail_action[0]);
316
317 /* A pointer to an array of SC_ACTION structures. */
318 service_fail_action.lpsaActions = fail_action;
319
320 if (!ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS,
321 &service_fail_action)) {
322 char *msg_buf = ovs_lasterror_to_string();
323 VLOG_FATAL("Failed to configure service fail actions (%s).", msg_buf);
324 }
325 }
326
327 /* When a daemon is passed the --detach option, we create a new
328 * process and pass an additional non-documented option called --pipe-handle.
329 * Through this option, the parent passes one end of a pipe handle. */
330 void
331 set_pipe_handle(const char *pipe_handle)
332 {
333 write_handle = (HANDLE) atoi(pipe_handle);
334 }
335
336 /* If one of the command line option is "--detach", creates
337 * a new process in case of parent, waits for child to start and exits.
338 * In case of the child, returns. */
339 static bool
340 detach_process(int argc, char *argv[])
341 {
342 SECURITY_ATTRIBUTES sa;
343 STARTUPINFO si;
344 PROCESS_INFORMATION pi;
345 HANDLE read_pipe, write_pipe;
346 char *buffer;
347 int error, i;
348 char ch;
349
350 /* We are only interested in the '--detach' and '--pipe-handle'. */
351 for (i = 0; i < argc; i ++) {
352 if (!strcmp(argv[i], "--detach")) {
353 detach = true;
354 } else if (!strncmp(argv[i], "--pipe-handle", 13)) {
355 /* If running as a child, return. */
356 detached = true;
357 return true;
358 }
359 }
360
361 /* Nothing to do if the option --detach is not set. */
362 if (!detach) {
363 return false;
364 }
365
366 /* Set the security attribute such that a process created will
367 * inherit the pipe handles. */
368 sa.nLength = sizeof(sa);
369 sa.lpSecurityDescriptor = NULL;
370 sa.bInheritHandle = TRUE;
371
372 /* Create an anonymous pipe to communicate with the child. */
373 error = CreatePipe(&read_pipe, &write_pipe, &sa, 0);
374 if (!error) {
375 VLOG_FATAL("CreatePipe failed (%s)", ovs_lasterror_to_string());
376 }
377
378 GetStartupInfo(&si);
379
380 /* To the child, we pass an extra argument '--pipe-handle=write_pipe' */
381 buffer = xasprintf("%s %s=%ld", GetCommandLine(), "--pipe-handle",
382 write_pipe);
383
384 /* Create a detached child */
385 error = CreateProcess(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS,
386 NULL, NULL, &si, &pi);
387 if (!error) {
388 VLOG_FATAL("CreateProcess failed (%s)", ovs_lasterror_to_string());
389 }
390
391 /* Close one end of the pipe in the parent. */
392 CloseHandle(write_pipe);
393
394 /* Block and wait for child to say it is ready. */
395 error = ReadFile(read_pipe, &ch, 1, NULL, NULL);
396 if (!error) {
397 VLOG_FATAL("Failed to read from child (%s)",
398 ovs_lasterror_to_string());
399 }
400 /* The child has successfully started and is ready. */
401 exit(0);
402 }
403
404 static void
405 unlink_pidfile(void)
406 {
407 if (filep_pidfile) {
408 fclose(filep_pidfile);
409 }
410 if (pidfile) {
411 unlink(pidfile);
412 }
413 }
414
415 /* If a pidfile has been configured, creates it and stores the running
416 * process's pid in it. Ensures that the pidfile will be deleted when the
417 * process exits. */
418 static void
419 make_pidfile(void)
420 {
421 int error;
422
423 error = GetFileAttributes(pidfile);
424 if (error != INVALID_FILE_ATTRIBUTES) {
425 /* pidfile exists. Try to unlink() it. */
426 error = unlink(pidfile);
427 if (error) {
428 VLOG_FATAL("Failed to delete existing pidfile %s (%s)", pidfile,
429 ovs_strerror(errno));
430 }
431 }
432
433 filep_pidfile = fopen(pidfile, "w");
434 if (filep_pidfile == NULL) {
435 VLOG_FATAL("failed to open %s (%s)", pidfile, ovs_strerror(errno));
436 }
437
438 fatal_signal_add_hook(unlink_pidfile, NULL, NULL, true);
439
440 fprintf(filep_pidfile, "%d\n", _getpid());
441 if (fflush(filep_pidfile) == EOF) {
442 VLOG_FATAL("Failed to write into the pidfile %s", pidfile);
443 }
444
445 /* Don't close the pidfile till the process exits. */
446 }
447
448 void daemonize_start(void)
449 {
450 if (pidfile) {
451 make_pidfile();
452 }
453 }
454
455 void
456 daemonize_complete(void)
457 {
458 /* If running as a child because '--detach' option was specified,
459 * communicate with the parent to inform that the child is ready. */
460 if (detached) {
461 int error;
462
463 close_standard_fds();
464
465 error = WriteFile(write_handle, "a", 1, NULL, NULL);
466 if (!error) {
467 VLOG_FATAL("Failed to communicate with the parent (%s)",
468 ovs_lasterror_to_string());
469 }
470 }
471
472 service_complete();
473 }
474
475 /* Returns the file name that would be used for a pidfile if 'name' were
476 * provided to set_pidfile(). The caller must free the returned string. */
477 char *
478 make_pidfile_name(const char *name)
479 {
480 if (name && strchr(name, ':')) {
481 return xstrdup(name);
482 } else {
483 return xasprintf("%s/%s.pid", ovs_rundir(), program_name);
484 }
485 }