daemon-windows: Rename service variables.
[sliver-openvswitch.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 <stdio.h>
20 #include <stdlib.h>
21 #include "poll-loop.h"
22 #include "vlog.h"
23
24 VLOG_DEFINE_THIS_MODULE(daemon);
25
26 static bool service_create;          /* Was --service specified? */
27 static bool service_started;         /* Have we dispatched service to start? */
28
29 /* --service-monitor: Should the service be restarted if it dies
30  * unexpectedly? */
31 static bool monitor;
32
33 /* Handle to the Services Manager and the created service. */
34 static SC_HANDLE manager, service;
35
36 /* Handle to the status information structure for the current service. */
37 static SERVICE_STATUS_HANDLE hstatus;
38
39 /* Hold the service's current status. */
40 static SERVICE_STATUS service_status;
41
42 /* Handle to an event object used to wakeup from poll_block(). */
43 static HANDLE wevent;
44
45 /* Hold the arguments sent to the main function. */
46 static int sargc;
47 static char ***sargvp;
48
49 static void check_service(void);
50 static void handle_scm_callback(void);
51 static void init_service_status(void);
52 static void set_config_failure_actions(void);
53
54 extern int main(int argc, char *argv[]);
55
56 void
57 daemon_usage(void)
58 {
59     printf(
60         "\nService options:\n"
61         "  --service               run in background as a service.\n"
62         "  --service-monitor       restart the service in case of an "
63                                    "unexpected failure. \n",
64         ovs_rundir(), program_name);
65 }
66
67 /* Registers the call-back and configures the actions in case of a failure
68  * with the Windows services manager. */
69 void
70 service_start(int *argcp, char **argvp[])
71 {
72     int argc = *argcp;
73     char **argv = *argvp;
74     int i;
75     SERVICE_TABLE_ENTRY service_table[] = {
76         {(LPTSTR)program_name, (LPSERVICE_MAIN_FUNCTION)main},
77         {NULL, NULL}
78     };
79
80     /* 'service_started' is 'false' when service_start() is called the first
81      * time.  It is 'true', when it is called the second time by the Windows
82      * services manager. */
83     if (service_started) {
84         init_service_status();
85
86         wevent = CreateEvent(NULL, TRUE, FALSE, NULL);
87         if (!wevent) {
88             char *msg_buf = ovs_lasterror_to_string();
89             VLOG_FATAL("Failed to create a event (%s).", msg_buf);
90         }
91
92         poll_fd_wait_event(0, wevent, POLLIN);
93
94         /* Register the control handler. This function is called by the service
95          * manager to stop the service. */
96         hstatus = RegisterServiceCtrlHandler(program_name,
97                                          (LPHANDLER_FUNCTION)control_handler);
98         if (!hstatus) {
99             char *msg_buf = ovs_lasterror_to_string();
100             VLOG_FATAL("Failed to register the service control handler (%s).",
101                         msg_buf);
102         }
103
104         if (monitor) {
105             set_config_failure_actions();
106         }
107
108         /* When the service control manager does the call back, it does not
109          * send the same arguments as sent to the main function during the
110          * service start. So, use the arguments passed over during the first
111          * time. */
112         *argcp = sargc;
113         *argvp = *sargvp;
114
115         /* XXX: Windows implementation cannot have a unixctl commands in the
116         * traditional sense of unix domain sockets. If an implementation is
117         * done that involves 'unixctl' vlog commands the following call is
118         * needed to make sure that the unixctl commands for vlog get
119         * registered in a daemon, even before the first log message. */
120         vlog_init();
121
122         return;
123     }
124
125     assert_single_threaded();
126
127     /* A reference to arguments passed to the main function the first time.
128      * We need it after the call-back from service control manager. */
129     sargc = argc;
130     sargvp = argvp;
131
132     /* We are only interested in the '--service' and '--service-monitor'
133      * options before the call-back from the service control manager. */
134     for (i = 0; i < argc; i ++) {
135         if (!strcmp(argv[i], "--service")) {
136             service_create = true;
137         } else if (!strcmp(argv[i], "--service-monitor")) {
138             monitor = true;
139         }
140     }
141
142     /* If '--service' is not a command line option, run in foreground. */
143     if (!service_create) {
144         return;
145     }
146
147     /* If we have been configured to run as a service, then that service
148      * should already have been created either manually or through a start up
149      * script. */
150     check_service();
151
152     service_started = true;
153
154     /* StartServiceCtrlDispatcher blocks and returns after the service is
155      * stopped. */
156     if (!StartServiceCtrlDispatcher(service_table)) {
157         char *msg_buf = ovs_lasterror_to_string();
158         VLOG_FATAL("Failed at StartServiceCtrlDispatcher (%s)", msg_buf);
159     }
160     exit(0);
161 }
162
163 /* This function is registered with the Windows services manager through
164  * a call to RegisterServiceCtrlHandler() and will be called by the Windows
165  * services manager asynchronously to stop the service. */
166 void
167 control_handler(DWORD request)
168 {
169     switch (request) {
170     case SERVICE_CONTROL_STOP:
171     case SERVICE_CONTROL_SHUTDOWN:
172         service_status.dwCurrentState = SERVICE_STOPPED;
173         service_status.dwWin32ExitCode = NO_ERROR;
174         SetEvent(wevent);
175         break;
176
177     default:
178         break;
179     }
180 }
181
182 /* Return 'true' if the Windows services manager has called the
183  * control_handler() and asked the program to terminate. */
184 bool
185 should_service_stop(void)
186 {
187     if (service_started) {
188         if (service_status.dwCurrentState != SERVICE_RUNNING) {
189             return true;
190         } else {
191             poll_fd_wait_event(0, wevent, POLLIN);
192         }
193     }
194     return false;
195 }
196
197 /* Set the service as stopped. The control manager will terminate the
198  * service soon after this call. Hence, this should ideally be the last
199  * call before termination. */
200 void
201 service_stop()
202 {
203     ResetEvent(wevent);
204     CloseHandle(wevent);
205
206     service_status.dwCurrentState = SERVICE_STOPPED;
207     service_status.dwWin32ExitCode = NO_ERROR;
208     SetServiceStatus(hstatus, &service_status);
209 }
210
211 /* Call this function to signal that the daemon is ready. init_service()
212  * or control_handler() has already initalized/set the
213  * service_status.dwCurrentState .*/
214 static void
215 service_complete(void)
216 {
217     if (hstatus) {
218         SetServiceStatus(hstatus, &service_status);
219     }
220 }
221
222 /* Check whether 'program_name' has been created as a service. */
223 static void
224 check_service()
225 {
226     /* Establish a connection to the local service control manager. */
227     manager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
228     if (!manager) {
229         char *msg_buf = ovs_lasterror_to_string();
230         VLOG_FATAL("Failed to open the service control manager (%s).",
231                    msg_buf);
232     }
233
234     service = OpenService(manager, program_name, SERVICE_ALL_ACCESS);
235     if (!service) {
236         char *msg_buf = ovs_lasterror_to_string();
237         VLOG_FATAL("Failed to open service (%s).", msg_buf);
238     }
239 }
240
241 /* Service status of a service can be checked asynchronously through
242  * tools like 'sc' or through Windows services manager and is set
243  * through a call to SetServiceStatus(). */
244 static void
245 init_service_status()
246 {
247     /* The service runs in its own process. */
248     service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
249
250     /* The control codes the service accepts. */
251     service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
252                                             SERVICE_ACCEPT_SHUTDOWN;
253
254     /* Initialize the current state as SERVICE_RUNNING. */
255     service_status.dwCurrentState = SERVICE_RUNNING;
256
257     /* The exit code to indicate if there was an error. */
258     service_status.dwWin32ExitCode = NO_ERROR;
259
260     /* The checkpoint value the service increments periodically. Set as 0
261      * as we do not plan to periodically increment the value. */
262     service_status.dwCheckPoint = 0;
263
264     /* The estimated time required for the stop operation in ms. */
265     service_status.dwWaitHint = 1000;
266 }
267
268 /* In case of an unexpected termination, configure the action to be
269  * taken. */
270 static void
271 set_config_failure_actions()
272 {
273     /* In case of a failure, restart the process the first two times
274      * After 'dwResetPeriod', the failure count is reset. */
275     SC_ACTION fail_action[3] = {
276         {SC_ACTION_RESTART, 0},
277         {SC_ACTION_RESTART, 0},
278         {SC_ACTION_NONE, 0}
279     };
280     SERVICE_FAILURE_ACTIONS service_fail_action;
281
282     /* Reset failure count after (in seconds). */
283     service_fail_action.dwResetPeriod = 10;
284
285     /* Reboot message. */
286     service_fail_action.lpRebootMsg = NULL;
287
288     /* The command line of the process. */
289     service_fail_action.lpCommand = NULL;
290
291     /* Number of elements in 'fail_actions'. */
292     service_fail_action.cActions = sizeof(fail_action)/sizeof(fail_action[0]);
293
294     /* A pointer to an array of SC_ACTION structures. */
295     service_fail_action.lpsaActions = fail_action;
296
297     if (!ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS,
298                               &service_fail_action)) {
299         char *msg_buf = ovs_lasterror_to_string();
300         VLOG_FATAL("Failed to configure service fail actions (%s).", msg_buf);
301     }
302 }
303
304 \f
305 /* Stub functions to handle daemonize related calls in non-windows platform. */
306 bool
307 get_detach()
308 {
309     return false;
310 }
311
312 void
313 daemon_save_fd(int fd OVS_UNUSED)
314 {
315 }
316
317 void
318 daemonize(void)
319 {
320 }
321
322 void daemonize_start(void)
323 {
324 }
325
326 void
327 daemonize_complete(void)
328 {
329     service_complete();
330 }