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