2 * Copyright (c) 2014 Nicira, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include "poll-loop.h"
24 #pragma comment(lib, "advapi32")
26 VLOG_DEFINE_THIS_MODULE(daemon);
28 static bool detach; /* Was --service specified? */
29 static bool detached; /* Have we already detached? */
31 /* --service-monitor: Should the service be restarted if it dies
35 /* Handle to the Services Manager and the created service. */
36 static SC_HANDLE manager, service;
38 /* Handle to the status information structure for the current service. */
39 static SERVICE_STATUS_HANDLE hstatus;
41 /* Hold the service's current status. */
42 static SERVICE_STATUS service_status;
44 /* Handle to an event object used to wakeup from poll_block(). */
47 /* Hold the arguments sent to the main function. */
49 static char ***sargvp;
51 static void check_service(void);
52 static void handle_scm_callback(void);
53 static void init_service_status(void);
54 static void set_config_failure_actions(void);
56 extern int main(int argc, char *argv[]);
62 "\nService options:\n"
63 " --service run in background as a service.\n"
64 " --service-monitor restart the service in case of an "
65 "unexpected failure. \n",
66 ovs_rundir(), program_name);
69 /* Registers the call-back and configures the actions in case of a failure
70 * with the Windows services manager. */
72 service_start(int *argcp, char **argvp[])
77 SERVICE_TABLE_ENTRY service_table[] = {
78 {(LPTSTR)program_name, (LPSERVICE_MAIN_FUNCTION)main},
82 /* 'detached' is 'false' when service_start() is called the first time.
83 * It is 'true', when it is called the second time by the Windows services
86 init_service_status();
88 wevent = CreateEvent(NULL, TRUE, FALSE, NULL);
90 char *msg_buf = ovs_lasterror_to_string();
91 VLOG_FATAL("Failed to create a event (%s).", msg_buf);
94 poll_fd_wait_event(0, wevent, POLLIN);
96 /* Register the control handler. This function is called by the service
97 * manager to stop the service. */
98 hstatus = RegisterServiceCtrlHandler(program_name,
99 (LPHANDLER_FUNCTION)control_handler);
101 char *msg_buf = ovs_lasterror_to_string();
102 VLOG_FATAL("Failed to register the service control handler (%s).",
107 set_config_failure_actions();
110 /* When the service control manager does the call back, it does not
111 * send the same arguments as sent to the main function during the
112 * service start. So, use the arguments passed over during the first
117 /* XXX: Windows implementation cannot have a unixctl commands in the
118 * traditional sense of unix domain sockets. If an implementation is
119 * done that involves 'unixctl' vlog commands the following call is
120 * needed to make sure that the unixctl commands for vlog get
121 * registered in a daemon, even before the first log message. */
127 assert_single_threaded();
129 /* A reference to arguments passed to the main function the first time.
130 * We need it after the call-back from service control manager. */
134 /* We are only interested in the '--service' and '--service-monitor'
135 * options before the call-back from the service control manager. */
136 for (i = 0; i < argc; i ++) {
137 if (!strcmp(argv[i], "--service")) {
139 } else if (!strcmp(argv[i], "--service-monitor")) {
144 /* If '--service' is not a command line option, run in foreground. */
149 /* If we have been configured to run as a service, then that service
150 * should already have been created either manually or through a start up
156 /* StartServiceCtrlDispatcher blocks and returns after the service is
158 if (!StartServiceCtrlDispatcher(service_table)) {
159 char *msg_buf = ovs_lasterror_to_string();
160 VLOG_FATAL("Failed at StartServiceCtrlDispatcher (%s)", msg_buf);
165 /* This function is registered with the Windows services manager through
166 * a call to RegisterServiceCtrlHandler() and will be called by the Windows
167 * services manager asynchronously to stop the service. */
169 control_handler(DWORD request)
172 case SERVICE_CONTROL_STOP:
173 case SERVICE_CONTROL_SHUTDOWN:
174 service_status.dwCurrentState = SERVICE_STOPPED;
175 service_status.dwWin32ExitCode = NO_ERROR;
184 /* Return 'true' if the Windows services manager has called the
185 * control_handler() and asked the program to terminate. */
187 should_service_stop(void)
190 if (service_status.dwCurrentState != SERVICE_RUNNING) {
193 poll_fd_wait_event(0, wevent, POLLIN);
199 /* Set the service as stopped. The control manager will terminate the
200 * service soon after this call. Hence, this should ideally be the last
201 * call before termination. */
208 service_status.dwCurrentState = SERVICE_STOPPED;
209 service_status.dwWin32ExitCode = NO_ERROR;
210 SetServiceStatus(hstatus, &service_status);
213 /* Call this function to signal that the daemon is ready. init_service()
214 * or control_handler() has already initalized/set the
215 * service_status.dwCurrentState .*/
217 service_complete(void)
220 SetServiceStatus(hstatus, &service_status);
224 /* Check whether 'program_name' has been created as a service. */
228 /* Establish a connection to the local service control manager. */
229 manager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
231 char *msg_buf = ovs_lasterror_to_string();
232 VLOG_FATAL("Failed to open the service control manager (%s).",
236 service = OpenService(manager, program_name, SERVICE_ALL_ACCESS);
238 char *msg_buf = ovs_lasterror_to_string();
239 VLOG_FATAL("Failed to open service (%s).", msg_buf);
243 /* Service status of a service can be checked asynchronously through
244 * tools like 'sc' or through Windows services manager and is set
245 * through a call to SetServiceStatus(). */
247 init_service_status()
249 /* The service runs in its own process. */
250 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
252 /* The control codes the service accepts. */
253 service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
254 SERVICE_ACCEPT_SHUTDOWN;
256 /* Initialize the current state as SERVICE_RUNNING. */
257 service_status.dwCurrentState = SERVICE_RUNNING;
259 /* The exit code to indicate if there was an error. */
260 service_status.dwWin32ExitCode = NO_ERROR;
262 /* The checkpoint value the service increments periodically. Set as 0
263 * as we do not plan to periodically increment the value. */
264 service_status.dwCheckPoint = 0;
266 /* The estimated time required for the stop operation in ms. */
267 service_status.dwWaitHint = 1000;
270 /* In case of an unexpected termination, configure the action to be
273 set_config_failure_actions()
275 /* In case of a failure, restart the process the first two times
276 * After 'dwResetPeriod', the failure count is reset. */
277 SC_ACTION fail_action[3] = {
278 {SC_ACTION_RESTART, 0},
279 {SC_ACTION_RESTART, 0},
282 SERVICE_FAILURE_ACTIONS service_fail_action;
284 /* Reset failure count after (in seconds). */
285 service_fail_action.dwResetPeriod = 10;
287 /* Reboot message. */
288 service_fail_action.lpRebootMsg = NULL;
290 /* The command line of the process. */
291 service_fail_action.lpCommand = NULL;
293 /* Number of elements in 'fail_actions'. */
294 service_fail_action.cActions = sizeof(fail_action)/sizeof(fail_action[0]);
296 /* A pointer to an array of SC_ACTION structures. */
297 service_fail_action.lpsaActions = fail_action;
299 if (!ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS,
300 &service_fail_action)) {
301 char *msg_buf = ovs_lasterror_to_string();
302 VLOG_FATAL("Failed to configure service fail actions (%s).", msg_buf);
307 /* Stub functions to handle daemonize related calls in non-windows platform. */
315 daemon_save_fd(int fd OVS_UNUSED)
324 void daemonize_start(void)
329 daemonize_complete(void)