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