]>
Commit | Line | Data |
---|---|---|
fda546bd | 1 | /* |
133fb2c7 | 2 | * Copyright (c) 2014, 2017 Nicira, Inc. |
fda546bd GS |
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" | |
3834bcf2 | 19 | #include "daemon-private.h" |
fda546bd | 20 | #include <stdio.h> |
568b9dc8 | 21 | #include <io.h> |
fda546bd | 22 | #include <stdlib.h> |
133fb2c7 | 23 | #include <unistd.h> |
3f97b664 | 24 | #include "dirs.h" |
133fb2c7 | 25 | #include "fatal-signal.h" |
94a72fd6 | 26 | #include "ovs-thread.h" |
fd016ae3 | 27 | #include "openvswitch/poll-loop.h" |
e6211adc | 28 | #include "openvswitch/vlog.h" |
fda546bd | 29 | |
a91dc444 | 30 | VLOG_DEFINE_THIS_MODULE(daemon_windows); |
fda546bd | 31 | |
568b9dc8 PB |
32 | /* Constants for flock function */ |
33 | #define LOCK_SHARED 0x0 /* Shared lock. */ | |
34 | #define LOCK_UNLOCK 0x80000000 /* Unlock. Custom value. */ | |
35 | ||
69b17834 GS |
36 | static bool service_create; /* Was --service specified? */ |
37 | static bool service_started; /* Have we dispatched service to start? */ | |
fda546bd GS |
38 | |
39 | /* --service-monitor: Should the service be restarted if it dies | |
40 | * unexpectedly? */ | |
41 | static bool monitor; | |
42 | ||
3834bcf2 GS |
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. */ | |
4ec4776c | 46 | |
3834bcf2 GS |
47 | char *pidfile; /* --pidfile: Name of pidfile (null if none). */ |
48 | static FILE *filep_pidfile; /* File pointer to access the pidfile. */ | |
a9e9db79 | 49 | |
fda546bd GS |
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 | ||
4ec4776c GS |
71 | static bool detach_process(int argc, char *argv[]); |
72 | ||
fda546bd GS |
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 " | |
1b92d314 | 82 | "unexpected failure. \n"); |
fda546bd GS |
83 | } |
84 | ||
2ef3f753 AGS |
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 | ||
fda546bd GS |
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 | ||
4ec4776c GS |
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 | ||
69b17834 GS |
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) { | |
fda546bd GS |
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 | ||
1ca3348e | 126 | poll_wevent_wait(wevent); |
fda546bd GS |
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 | ||
44820d2a AGS |
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 | ||
fda546bd GS |
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")) { | |
69b17834 | 169 | service_create = true; |
fda546bd GS |
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. */ | |
69b17834 | 176 | if (!service_create) { |
fda546bd GS |
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 | ||
69b17834 | 185 | service_started = true; |
fda546bd GS |
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); | |
e2d12c07 | 208 | SetServiceStatus(hstatus, &service_status); |
fda546bd GS |
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 | { | |
69b17834 | 221 | if (service_started) { |
fda546bd GS |
222 | if (service_status.dwCurrentState != SERVICE_RUNNING) { |
223 | return true; | |
224 | } else { | |
1ca3348e | 225 | poll_wevent_wait(wevent); |
fda546bd GS |
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 | { | |
02a514ef GS |
237 | if (!service_started) { |
238 | return; | |
239 | } | |
240 | fatal_signal_atexit_handler(); | |
241 | ||
fda546bd GS |
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 | ||
4ec4776c GS |
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 ++) { | |
2ef3f753 | 368 | if (!detach && !strcmp(argv[i], "--detach")) { |
4ec4776c GS |
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 | ||
568b9dc8 PB |
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).", | |
9364ae65 | 429 | ovs_strerror(errno)); |
568b9dc8 PB |
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 | ||
a9e9db79 GS |
448 | static void |
449 | unlink_pidfile(void) | |
450 | { | |
451 | if (filep_pidfile) { | |
568b9dc8 PB |
452 | /* Remove the shared lock on file */ |
453 | flock(filep_pidfile, LOCK_UNLOCK); | |
a9e9db79 GS |
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 | ||
568b9dc8 PB |
484 | flock(filep_pidfile, LOCKFILE_EXCLUSIVE_LOCK); |
485 | ||
a9e9db79 GS |
486 | fatal_signal_add_hook(unlink_pidfile, NULL, NULL, true); |
487 | ||
133fb2c7 | 488 | fprintf(filep_pidfile, "%ld\n", (long int) getpid()); |
a9e9db79 GS |
489 | if (fflush(filep_pidfile) == EOF) { |
490 | VLOG_FATAL("Failed to write into the pidfile %s", pidfile); | |
491 | } | |
492 | ||
568b9dc8 PB |
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 | ||
a9e9db79 | 497 | /* Don't close the pidfile till the process exits. */ |
fda546bd GS |
498 | } |
499 | ||
e91b927d AZ |
500 | void |
501 | daemonize_start(bool access_datapath OVS_UNUSED) | |
fda546bd | 502 | { |
a9e9db79 GS |
503 | if (pidfile) { |
504 | make_pidfile(); | |
505 | } | |
fda546bd GS |
506 | } |
507 | ||
508 | void | |
509 | daemonize_complete(void) | |
510 | { | |
4ec4776c GS |
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; | |
d6bc33f3 GS |
515 | |
516 | close_standard_fds(); | |
517 | ||
4ec4776c GS |
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 | ||
fda546bd GS |
525 | service_complete(); |
526 | } | |
a9e9db79 | 527 | |
e91b927d AZ |
528 | void |
529 | daemon_become_new_user(bool access_datapath OVS_UNUSED) | |
530 | { | |
531 | } | |
532 | ||
a9e9db79 GS |
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. */ | |
3834bcf2 | 535 | char * |
a9e9db79 GS |
536 | make_pidfile_name(const char *name) |
537 | { | |
5b73d2c0 PB |
538 | if (name) { |
539 | if (strchr(name, ':')) { | |
540 | return xstrdup(name); | |
541 | } else { | |
542 | return xasprintf("%s/%s", ovs_rundir(), name); | |
543 | } | |
a9e9db79 GS |
544 | } else { |
545 | return xasprintf("%s/%s.pid", ovs_rundir(), program_name); | |
546 | } | |
547 | } | |
e91b927d AZ |
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 | } |