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