1eaad808eSchristos /*
2eaad808eSchristos * winrc/win_svc.c - windows services API implementation for unbound
3eaad808eSchristos *
4eaad808eSchristos * Copyright (c) 2009, NLnet Labs. All rights reserved.
5eaad808eSchristos *
6eaad808eSchristos * This software is open source.
7eaad808eSchristos *
8eaad808eSchristos * Redistribution and use in source and binary forms, with or without
9eaad808eSchristos * modification, are permitted provided that the following conditions
10eaad808eSchristos * are met:
11eaad808eSchristos *
12eaad808eSchristos * Redistributions of source code must retain the above copyright notice,
13eaad808eSchristos * this list of conditions and the following disclaimer.
14eaad808eSchristos *
15eaad808eSchristos * Redistributions in binary form must reproduce the above copyright notice,
16eaad808eSchristos * this list of conditions and the following disclaimer in the documentation
17eaad808eSchristos * and/or other materials provided with the distribution.
18eaad808eSchristos *
19eaad808eSchristos * Neither the name of the NLNET LABS nor the names of its contributors may
20eaad808eSchristos * be used to endorse or promote products derived from this software without
21eaad808eSchristos * specific prior written permission.
22eaad808eSchristos *
23eaad808eSchristos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24eaad808eSchristos * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25eaad808eSchristos * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26eaad808eSchristos * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27eaad808eSchristos * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28eaad808eSchristos * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29eaad808eSchristos * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30eaad808eSchristos * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31eaad808eSchristos * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32eaad808eSchristos * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33eaad808eSchristos * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34eaad808eSchristos */
35eaad808eSchristos
36eaad808eSchristos /**
37eaad808eSchristos * \file
38eaad808eSchristos *
39eaad808eSchristos * This file contains functions to integrate with the windows services API.
40eaad808eSchristos * This means it handles the commandline switches to install and remove
41eaad808eSchristos * the service (via CreateService and DeleteService), it handles
42eaad808eSchristos * the ServiceMain() main service entry point when started as a service,
43eaad808eSchristos * and it handles the Handler[_ex]() to process requests to the service
44eaad808eSchristos * (such as start and stop and status).
45eaad808eSchristos */
46eaad808eSchristos #include "config.h"
47eaad808eSchristos #include "winrc/win_svc.h"
48eaad808eSchristos #include "winrc/w_inst.h"
49eaad808eSchristos #include "daemon/daemon.h"
50eaad808eSchristos #include "daemon/worker.h"
51eaad808eSchristos #include "daemon/remote.h"
52eaad808eSchristos #include "util/config_file.h"
53eaad808eSchristos #include "util/netevent.h"
54eaad808eSchristos #include "util/ub_event.h"
55*d6959bcfSchristos #include "util/net_help.h"
56eaad808eSchristos
57eaad808eSchristos /** global service status */
58eaad808eSchristos static SERVICE_STATUS service_status;
59eaad808eSchristos /** global service status handle */
60eaad808eSchristos static SERVICE_STATUS_HANDLE service_status_handle;
61eaad808eSchristos /** global service stop event */
62eaad808eSchristos static WSAEVENT service_stop_event = NULL;
63eaad808eSchristos /** event struct for stop callbacks */
64eaad808eSchristos static struct ub_event* service_stop_ev = NULL;
65eaad808eSchristos /** if stop even means shutdown or restart */
66eaad808eSchristos static int service_stop_shutdown = 0;
67eaad808eSchristos /** config file to open. global communication to service_main() */
68eaad808eSchristos static char* service_cfgfile = CONFIGFILE;
69eaad808eSchristos /** commandline verbosity. global communication to service_main() */
70eaad808eSchristos static int service_cmdline_verbose = 0;
71eaad808eSchristos /** the cron callback */
72eaad808eSchristos static struct comm_timer* service_cron = NULL;
73eaad808eSchristos /** the cron thread */
74762909a6Schristos static ub_thread_type cron_thread = NULL;
75eaad808eSchristos /** if cron has already done its quick check */
76eaad808eSchristos static int cron_was_quick = 0;
77eaad808eSchristos
78eaad808eSchristos /**
79eaad808eSchristos * Report current service status to service control manager
80eaad808eSchristos * @param state: current state
81eaad808eSchristos * @param exitcode: error code (when stopped)
82eaad808eSchristos * @param wait: pending operation estimated time in milliseconds.
83eaad808eSchristos */
report_status(DWORD state,DWORD exitcode,DWORD wait)84eaad808eSchristos static void report_status(DWORD state, DWORD exitcode, DWORD wait)
85eaad808eSchristos {
86eaad808eSchristos static DWORD checkpoint = 1;
87eaad808eSchristos service_status.dwCurrentState = state;
88eaad808eSchristos service_status.dwWin32ExitCode = exitcode;
89eaad808eSchristos service_status.dwWaitHint = wait;
90eaad808eSchristos if(state == SERVICE_START_PENDING)
91eaad808eSchristos service_status.dwControlsAccepted = 0;
92eaad808eSchristos else service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
93eaad808eSchristos if(state == SERVICE_RUNNING || state == SERVICE_STOPPED)
94eaad808eSchristos service_status.dwCheckPoint = 0;
95eaad808eSchristos else service_status.dwCheckPoint = checkpoint++;
96eaad808eSchristos SetServiceStatus(service_status_handle, &service_status);
97eaad808eSchristos }
98eaad808eSchristos
99eaad808eSchristos /**
100eaad808eSchristos * Service control handler. Called by serviceControlManager when a control
101eaad808eSchristos * code is sent to the service (with ControlService).
102eaad808eSchristos * @param ctrl: control code
103eaad808eSchristos */
104eaad808eSchristos static void
hdlr(DWORD ctrl)105eaad808eSchristos hdlr(DWORD ctrl)
106eaad808eSchristos {
107eaad808eSchristos if(ctrl == SERVICE_CONTROL_STOP) {
108eaad808eSchristos report_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
109eaad808eSchristos service_stop_shutdown = 1;
110eaad808eSchristos /* send signal to stop */
111eaad808eSchristos if(!WSASetEvent(service_stop_event))
112eaad808eSchristos log_err("Could not WSASetEvent: %s",
113eaad808eSchristos wsa_strerror(WSAGetLastError()));
114eaad808eSchristos return;
115eaad808eSchristos } else {
116eaad808eSchristos /* ctrl == SERVICE_CONTROL_INTERROGATE or whatever */
117eaad808eSchristos /* update status */
118eaad808eSchristos report_status(service_status.dwCurrentState, NO_ERROR, 0);
119eaad808eSchristos }
120eaad808eSchristos }
121eaad808eSchristos
122eaad808eSchristos /**
123eaad808eSchristos * report event to system event log
124eaad808eSchristos * For use during startup and shutdown.
125eaad808eSchristos * @param str: the error
126eaad808eSchristos */
127eaad808eSchristos static void
reportev(const char * str)128eaad808eSchristos reportev(const char* str)
129eaad808eSchristos {
130eaad808eSchristos char b[256];
131eaad808eSchristos char e[256];
132eaad808eSchristos HANDLE* s;
133eaad808eSchristos LPCTSTR msg = b;
134eaad808eSchristos /* print quickly to keep GetLastError value */
135eaad808eSchristos wsvc_err2str(e, sizeof(e), str, GetLastError());
136eaad808eSchristos snprintf(b, sizeof(b), "%s: %s", SERVICE_NAME, e);
137eaad808eSchristos s = RegisterEventSource(NULL, SERVICE_NAME);
138eaad808eSchristos if(!s) return;
139eaad808eSchristos ReportEvent(s, /* event log */
140eaad808eSchristos EVENTLOG_ERROR_TYPE, /* event type */
141eaad808eSchristos 0, /* event category */
142eaad808eSchristos MSG_GENERIC_ERR, /* event ID (from gen_msg.mc) */
143eaad808eSchristos NULL, /* user security context */
144eaad808eSchristos 1, /* numstrings */
145eaad808eSchristos 0, /* binary size */
146eaad808eSchristos &msg, /* strings */
147eaad808eSchristos NULL); /* binary data */
148eaad808eSchristos DeregisterEventSource(s);
149eaad808eSchristos }
150eaad808eSchristos
151eaad808eSchristos /**
152eaad808eSchristos * Obtain registry string (if it exists).
153eaad808eSchristos * @param key: key string
154eaad808eSchristos * @param name: name of value to fetch.
155eaad808eSchristos * @return malloced string with the result or NULL if it did not
156eaad808eSchristos * exist on an error (logged) was encountered.
157eaad808eSchristos */
158eaad808eSchristos static char*
lookup_reg_str(const char * key,const char * name)159eaad808eSchristos lookup_reg_str(const char* key, const char* name)
160eaad808eSchristos {
161eaad808eSchristos HKEY hk = NULL;
162eaad808eSchristos DWORD type = 0;
163eaad808eSchristos BYTE buf[1024];
164eaad808eSchristos DWORD len = (DWORD)sizeof(buf);
165eaad808eSchristos LONG ret;
166eaad808eSchristos char* result = NULL;
167eaad808eSchristos ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hk);
168eaad808eSchristos if(ret == ERROR_FILE_NOT_FOUND)
169eaad808eSchristos return NULL; /* key does not exist */
170eaad808eSchristos else if(ret != ERROR_SUCCESS) {
171eaad808eSchristos reportev("RegOpenKeyEx failed");
172eaad808eSchristos return NULL;
173eaad808eSchristos }
174eaad808eSchristos ret = RegQueryValueEx(hk, (LPCTSTR)name, 0, &type, buf, &len);
175eaad808eSchristos if(RegCloseKey(hk))
176eaad808eSchristos reportev("RegCloseKey");
177eaad808eSchristos if(ret == ERROR_FILE_NOT_FOUND)
178eaad808eSchristos return NULL; /* name does not exist */
179eaad808eSchristos else if(ret != ERROR_SUCCESS) {
180eaad808eSchristos reportev("RegQueryValueEx failed");
181eaad808eSchristos return NULL;
182eaad808eSchristos }
183eaad808eSchristos if(type == REG_SZ || type == REG_MULTI_SZ || type == REG_EXPAND_SZ) {
184eaad808eSchristos buf[sizeof(buf)-1] = 0;
185eaad808eSchristos buf[sizeof(buf)-2] = 0; /* for multi_sz */
186eaad808eSchristos result = strdup((char*)buf);
187eaad808eSchristos if(!result) reportev("out of memory");
188eaad808eSchristos }
189eaad808eSchristos return result;
190eaad808eSchristos }
191eaad808eSchristos
192eaad808eSchristos /**
193eaad808eSchristos * Obtain registry integer (if it exists).
194eaad808eSchristos * @param key: key string
195eaad808eSchristos * @param name: name of value to fetch.
196eaad808eSchristos * @return integer value (if it exists), or 0 on error.
197eaad808eSchristos */
198eaad808eSchristos static int
lookup_reg_int(const char * key,const char * name)199eaad808eSchristos lookup_reg_int(const char* key, const char* name)
200eaad808eSchristos {
201eaad808eSchristos HKEY hk = NULL;
202eaad808eSchristos DWORD type = 0;
203eaad808eSchristos BYTE buf[1024];
204eaad808eSchristos DWORD len = (DWORD)sizeof(buf);
205eaad808eSchristos LONG ret;
206eaad808eSchristos int result = 0;
207eaad808eSchristos ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hk);
208eaad808eSchristos if(ret == ERROR_FILE_NOT_FOUND)
209eaad808eSchristos return 0; /* key does not exist */
210eaad808eSchristos else if(ret != ERROR_SUCCESS) {
211eaad808eSchristos reportev("RegOpenKeyEx failed");
212eaad808eSchristos return 0;
213eaad808eSchristos }
214eaad808eSchristos ret = RegQueryValueEx(hk, (LPCTSTR)name, 0, &type, buf, &len);
215eaad808eSchristos if(RegCloseKey(hk))
216eaad808eSchristos reportev("RegCloseKey");
217eaad808eSchristos if(ret == ERROR_FILE_NOT_FOUND)
218eaad808eSchristos return 0; /* name does not exist */
219eaad808eSchristos else if(ret != ERROR_SUCCESS) {
220eaad808eSchristos reportev("RegQueryValueEx failed");
221eaad808eSchristos return 0;
222eaad808eSchristos }
223eaad808eSchristos if(type == REG_SZ || type == REG_MULTI_SZ || type == REG_EXPAND_SZ) {
224eaad808eSchristos buf[sizeof(buf)-1] = 0;
225eaad808eSchristos buf[sizeof(buf)-2] = 0; /* for multi_sz */
226eaad808eSchristos result = atoi((char*)buf);
227eaad808eSchristos } else if(type == REG_DWORD) {
228eaad808eSchristos DWORD r;
229eaad808eSchristos memmove(&r, buf, sizeof(r));
230eaad808eSchristos result = r;
231eaad808eSchristos }
232eaad808eSchristos return result;
233eaad808eSchristos }
234eaad808eSchristos
235eaad808eSchristos /** wait for unbound-anchor process to finish */
236eaad808eSchristos static void
waitforubanchor(PROCESS_INFORMATION * pinfo)237eaad808eSchristos waitforubanchor(PROCESS_INFORMATION* pinfo)
238eaad808eSchristos {
239eaad808eSchristos /* we have 5 seconds scheduled for it, usually it will be very fast,
240eaad808eSchristos * with only a UDP message or two (100 msec or so), but the https
241eaad808eSchristos * connections could take some time */
242eaad808eSchristos DWORD count = 7900;
243eaad808eSchristos DWORD ret = WAIT_TIMEOUT;
244eaad808eSchristos /* decrease timer every 1/10 second, we are still starting up */
245eaad808eSchristos while(ret == WAIT_TIMEOUT) {
246eaad808eSchristos ret = WaitForSingleObject(pinfo->hProcess, 100);
247eaad808eSchristos if(count > 4000) count -= 100;
248eaad808eSchristos else count--; /* go slow, it is taking long */
249eaad808eSchristos if(count > 3000)
250eaad808eSchristos report_status(SERVICE_START_PENDING, NO_ERROR, count);
251eaad808eSchristos }
252eaad808eSchristos verbose(VERB_ALGO, "unbound-anchor done");
253eaad808eSchristos if(ret != WAIT_OBJECT_0) {
254eaad808eSchristos return; /* did not end successfully */
255eaad808eSchristos }
256eaad808eSchristos if(!GetExitCodeProcess(pinfo->hProcess, &ret)) {
257eaad808eSchristos log_err("GetExitCodeProcess failed");
258eaad808eSchristos return;
259eaad808eSchristos }
260eaad808eSchristos verbose(VERB_ALGO, "unbound-anchor exit code is %d", (int)ret);
261eaad808eSchristos if(ret != 0) {
262eaad808eSchristos log_info("The root trust anchor has been updated.");
263eaad808eSchristos }
264eaad808eSchristos }
265eaad808eSchristos
266eaad808eSchristos
267eaad808eSchristos /**
268eaad808eSchristos * Perform root anchor update if so configured, by calling that process
269eaad808eSchristos */
270eaad808eSchristos static void
call_root_update(void)271eaad808eSchristos call_root_update(void)
272eaad808eSchristos {
273eaad808eSchristos char* rootanchor;
274eaad808eSchristos rootanchor = lookup_reg_str("Software\\Unbound", "RootAnchor");
275eaad808eSchristos if(rootanchor && strlen(rootanchor)>0) {
276eaad808eSchristos STARTUPINFO sinfo;
277eaad808eSchristos PROCESS_INFORMATION pinfo;
278eaad808eSchristos memset(&pinfo, 0, sizeof(pinfo));
279eaad808eSchristos memset(&sinfo, 0, sizeof(sinfo));
280eaad808eSchristos sinfo.cb = sizeof(sinfo);
281eaad808eSchristos verbose(VERB_ALGO, "rootanchor: %s", rootanchor);
282eaad808eSchristos report_status(SERVICE_START_PENDING, NO_ERROR, 8000);
283eaad808eSchristos if(!CreateProcess(NULL, rootanchor, NULL, NULL, 0,
284eaad808eSchristos CREATE_NO_WINDOW, NULL, NULL, &sinfo, &pinfo))
285eaad808eSchristos log_err("CreateProcess error for unbound-anchor.exe");
286eaad808eSchristos else {
287eaad808eSchristos waitforubanchor(&pinfo);
288eaad808eSchristos CloseHandle(pinfo.hProcess);
289eaad808eSchristos CloseHandle(pinfo.hThread);
290eaad808eSchristos }
291eaad808eSchristos }
292eaad808eSchristos free(rootanchor);
293eaad808eSchristos }
294eaad808eSchristos
295eaad808eSchristos /**
296eaad808eSchristos * Init service. Keeps calling status pending to tell service control
297eaad808eSchristos * manager that this process is not hanging.
298eaad808eSchristos * @param r: restart, true on restart
299eaad808eSchristos * @param d: daemon returned here.
300eaad808eSchristos * @param c: config file returned here.
301eaad808eSchristos * @return false if failed.
302eaad808eSchristos */
303eaad808eSchristos static int
service_init(int r,struct daemon ** d,struct config_file ** c)304eaad808eSchristos service_init(int r, struct daemon** d, struct config_file** c)
305eaad808eSchristos {
306eaad808eSchristos struct config_file* cfg = NULL;
307eaad808eSchristos struct daemon* daemon = NULL;
308eaad808eSchristos
309eaad808eSchristos if(!service_cfgfile) {
310eaad808eSchristos char* newf = lookup_reg_str("Software\\Unbound", "ConfigFile");
311eaad808eSchristos if(newf) service_cfgfile = newf;
312eaad808eSchristos else service_cfgfile = strdup(CONFIGFILE);
313eaad808eSchristos if(!service_cfgfile) fatal_exit("out of memory");
314eaad808eSchristos }
315eaad808eSchristos
316eaad808eSchristos /* create daemon */
317eaad808eSchristos if(r) daemon = *d;
318eaad808eSchristos else daemon = daemon_init();
319eaad808eSchristos if(!daemon) return 0;
320eaad808eSchristos if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2800);
321eaad808eSchristos
322eaad808eSchristos /* read config */
323eaad808eSchristos cfg = config_create();
324eaad808eSchristos if(!cfg) return 0;
325eaad808eSchristos if(!config_read(cfg, service_cfgfile, daemon->chroot)) {
326eaad808eSchristos if(errno != ENOENT) {
327eaad808eSchristos log_err("error in config file");
328eaad808eSchristos return 0;
329eaad808eSchristos }
330eaad808eSchristos log_warn("could not open config file, using defaults");
331eaad808eSchristos }
332eaad808eSchristos if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2600);
333eaad808eSchristos
334eaad808eSchristos verbose(VERB_QUERY, "winservice - apply settings");
335eaad808eSchristos /* apply settings and init */
336eaad808eSchristos verbosity = cfg->verbosity + service_cmdline_verbose;
337eaad808eSchristos w_config_adjust_directory(cfg);
338eaad808eSchristos if(cfg->directory && cfg->directory[0]) {
339eaad808eSchristos char* dir = cfg->directory;
340eaad808eSchristos if(chdir(dir)) {
341eaad808eSchristos log_err("could not chdir to %s: %s",
342eaad808eSchristos dir, strerror(errno));
343eaad808eSchristos if(errno != ENOENT)
344eaad808eSchristos return 0;
345eaad808eSchristos log_warn("could not change directory - continuing");
346eaad808eSchristos } else
347eaad808eSchristos verbose(VERB_QUERY, "chdir to %s", dir);
348eaad808eSchristos }
349eaad808eSchristos log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
350eaad808eSchristos if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2400);
351eaad808eSchristos verbose(VERB_QUERY, "winservice - apply cfg");
352eaad808eSchristos daemon_apply_cfg(daemon, cfg);
353eaad808eSchristos
354eaad808eSchristos if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2300);
355eaad808eSchristos if(!(daemon->rc = daemon_remote_create(cfg))) {
356eaad808eSchristos log_err("could not set up remote-control");
357eaad808eSchristos daemon_delete(daemon);
358eaad808eSchristos config_delete(cfg);
359eaad808eSchristos return 0;
360eaad808eSchristos }
361*d6959bcfSchristos if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
362*d6959bcfSchristos if(!(daemon->listen_sslctx = listen_sslctx_create(
363*d6959bcfSchristos cfg->ssl_service_key, cfg->ssl_service_pem, NULL)))
364*d6959bcfSchristos fatal_exit("could not set up listen SSL_CTX");
365*d6959bcfSchristos }
366*d6959bcfSchristos if(!(daemon->connect_sslctx = connect_sslctx_create(NULL, NULL,
367*d6959bcfSchristos cfg->tls_cert_bundle, cfg->tls_win_cert)))
368*d6959bcfSchristos fatal_exit("could not set up connect SSL_CTX");
369eaad808eSchristos
370eaad808eSchristos /* open ports */
371eaad808eSchristos /* keep reporting that we are busy starting */
372eaad808eSchristos if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2200);
373eaad808eSchristos verbose(VERB_QUERY, "winservice - open ports");
374eaad808eSchristos if(!daemon_open_shared_ports(daemon)) return 0;
375eaad808eSchristos verbose(VERB_QUERY, "winservice - ports opened");
376eaad808eSchristos if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2000);
377eaad808eSchristos
378eaad808eSchristos *d = daemon;
379eaad808eSchristos *c = cfg;
380eaad808eSchristos return 1;
381eaad808eSchristos }
382eaad808eSchristos
383eaad808eSchristos /**
384eaad808eSchristos * Deinit the service
385eaad808eSchristos */
386eaad808eSchristos static void
service_deinit(struct daemon * daemon,struct config_file * cfg)387eaad808eSchristos service_deinit(struct daemon* daemon, struct config_file* cfg)
388eaad808eSchristos {
389eaad808eSchristos daemon_cleanup(daemon);
390eaad808eSchristos config_delete(cfg);
391eaad808eSchristos daemon_delete(daemon);
392eaad808eSchristos }
393eaad808eSchristos
394eaad808eSchristos #ifdef DOXYGEN
395eaad808eSchristos #define ATTR_UNUSED(x) x
396eaad808eSchristos #endif
397eaad808eSchristos /**
398eaad808eSchristos * The main function for the service.
399eaad808eSchristos * Called by the services API when starting unbound on windows in background.
400eaad808eSchristos * Arguments could have been present in the string 'path'.
401eaad808eSchristos * @param argc: nr args
402eaad808eSchristos * @param argv: arg text.
403eaad808eSchristos */
404eaad808eSchristos static void
service_main(DWORD ATTR_UNUSED (argc),LPTSTR * ATTR_UNUSED (argv))405eaad808eSchristos service_main(DWORD ATTR_UNUSED(argc), LPTSTR* ATTR_UNUSED(argv))
406eaad808eSchristos {
407eaad808eSchristos struct config_file* cfg = NULL;
408eaad808eSchristos struct daemon* daemon = NULL;
409eaad808eSchristos
410eaad808eSchristos service_status_handle = RegisterServiceCtrlHandler(SERVICE_NAME,
411eaad808eSchristos (LPHANDLER_FUNCTION)hdlr);
412eaad808eSchristos if(!service_status_handle) {
413eaad808eSchristos reportev("Could not RegisterServiceCtrlHandler");
414eaad808eSchristos return;
415eaad808eSchristos }
416eaad808eSchristos
417eaad808eSchristos service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
418eaad808eSchristos service_status.dwServiceSpecificExitCode = 0;
419eaad808eSchristos
420eaad808eSchristos /* see if we have root anchor update enabled */
421eaad808eSchristos call_root_update();
422eaad808eSchristos
423eaad808eSchristos /* we are now starting up */
424eaad808eSchristos report_status(SERVICE_START_PENDING, NO_ERROR, 3000);
425eaad808eSchristos if(!service_init(0, &daemon, &cfg)) {
426eaad808eSchristos reportev("Could not service_init");
427eaad808eSchristos report_status(SERVICE_STOPPED, NO_ERROR, 0);
428eaad808eSchristos return;
429eaad808eSchristos }
430eaad808eSchristos
431eaad808eSchristos /* event that gets signalled when we want to quit; it
432eaad808eSchristos * should get registered in the worker-0 waiting loop. */
433eaad808eSchristos service_stop_event = WSACreateEvent();
434eaad808eSchristos if(service_stop_event == WSA_INVALID_EVENT) {
435eaad808eSchristos log_err("WSACreateEvent: %s", wsa_strerror(WSAGetLastError()));
436eaad808eSchristos reportev("Could not WSACreateEvent");
437eaad808eSchristos report_status(SERVICE_STOPPED, NO_ERROR, 0);
438eaad808eSchristos return;
439eaad808eSchristos }
440eaad808eSchristos if(!WSAResetEvent(service_stop_event)) {
441eaad808eSchristos log_err("WSAResetEvent: %s", wsa_strerror(WSAGetLastError()));
442eaad808eSchristos }
443eaad808eSchristos
444eaad808eSchristos /* SetServiceStatus SERVICE_RUNNING;*/
445eaad808eSchristos report_status(SERVICE_RUNNING, NO_ERROR, 0);
446eaad808eSchristos verbose(VERB_QUERY, "winservice - init complete");
447eaad808eSchristos
448eaad808eSchristos /* daemon performs work */
449eaad808eSchristos while(!service_stop_shutdown) {
450eaad808eSchristos daemon_fork(daemon);
451eaad808eSchristos if(!service_stop_shutdown) {
452eaad808eSchristos daemon_cleanup(daemon);
453eaad808eSchristos config_delete(cfg); cfg=NULL;
454eaad808eSchristos if(!service_init(1, &daemon, &cfg)) {
455eaad808eSchristos reportev("Could not service_init");
456eaad808eSchristos report_status(SERVICE_STOPPED, NO_ERROR, 0);
457eaad808eSchristos return;
458eaad808eSchristos }
459eaad808eSchristos }
460eaad808eSchristos }
461eaad808eSchristos
462eaad808eSchristos /* exit */
463eaad808eSchristos verbose(VERB_ALGO, "winservice - cleanup.");
464eaad808eSchristos report_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
465eaad808eSchristos if(service_stop_event) (void)WSACloseEvent(service_stop_event);
466eaad808eSchristos service_deinit(daemon, cfg);
467eaad808eSchristos free(service_cfgfile);
468eaad808eSchristos verbose(VERB_QUERY, "winservice - full stop");
469eaad808eSchristos report_status(SERVICE_STOPPED, NO_ERROR, 0);
470eaad808eSchristos }
471eaad808eSchristos
472eaad808eSchristos /** start the service */
473eaad808eSchristos static void
service_start(const char * cfgfile,int v,int c)474eaad808eSchristos service_start(const char* cfgfile, int v, int c)
475eaad808eSchristos {
476eaad808eSchristos SERVICE_TABLE_ENTRY myservices[2] = {
477eaad808eSchristos {SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)service_main},
478eaad808eSchristos {NULL, NULL} };
479eaad808eSchristos verbosity=v;
480eaad808eSchristos if(verbosity >= VERB_QUERY) {
481eaad808eSchristos /* log to file about start sequence */
482eaad808eSchristos fclose(fopen("C:\\unbound.log", "w"));
483eaad808eSchristos log_init("C:\\unbound.log", 0, 0);
484eaad808eSchristos verbose(VERB_QUERY, "open logfile");
485eaad808eSchristos } else log_init(0, 1, 0); /* otherwise, use Application log */
486eaad808eSchristos if(c) {
487eaad808eSchristos service_cfgfile = strdup(cfgfile);
488eaad808eSchristos if(!service_cfgfile) fatal_exit("out of memory");
489eaad808eSchristos } else service_cfgfile = NULL;
490eaad808eSchristos service_cmdline_verbose = v;
491eaad808eSchristos /* this call returns when service has stopped. */
492eaad808eSchristos if(!StartServiceCtrlDispatcher(myservices)) {
493eaad808eSchristos reportev("Could not StartServiceCtrlDispatcher");
494eaad808eSchristos }
495eaad808eSchristos }
496eaad808eSchristos
497eaad808eSchristos void
wsvc_command_option(const char * wopt,const char * cfgfile,int v,int c)498eaad808eSchristos wsvc_command_option(const char* wopt, const char* cfgfile, int v, int c)
499eaad808eSchristos {
500eaad808eSchristos if(strcmp(wopt, "install") == 0)
501eaad808eSchristos wsvc_install(stdout, NULL);
502eaad808eSchristos else if(strcmp(wopt, "remove") == 0)
503eaad808eSchristos wsvc_remove(stdout);
504eaad808eSchristos else if(strcmp(wopt, "service") == 0)
505eaad808eSchristos service_start(cfgfile, v, c);
506eaad808eSchristos else if(strcmp(wopt, "start") == 0)
507eaad808eSchristos wsvc_rc_start(stdout);
508eaad808eSchristos else if(strcmp(wopt, "stop") == 0)
509eaad808eSchristos wsvc_rc_stop(stdout);
510eaad808eSchristos else fatal_exit("unknown option: %s", wopt);
511eaad808eSchristos exit(0);
512eaad808eSchristos }
513eaad808eSchristos
514eaad808eSchristos void
worker_win_stop_cb(int ATTR_UNUSED (fd),short ATTR_UNUSED (ev),void * arg)515eaad808eSchristos worker_win_stop_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), void* arg)
516eaad808eSchristos {
517eaad808eSchristos struct worker* worker = (struct worker*)arg;
518eaad808eSchristos verbose(VERB_QUERY, "caught stop signal (wsaevent)");
519eaad808eSchristos worker->need_to_exit = 1;
520eaad808eSchristos comm_base_exit(worker->base);
521eaad808eSchristos }
522eaad808eSchristos
523eaad808eSchristos /** wait for cron process to finish */
524eaad808eSchristos static void
waitforit(PROCESS_INFORMATION * pinfo)525eaad808eSchristos waitforit(PROCESS_INFORMATION* pinfo)
526eaad808eSchristos {
527eaad808eSchristos DWORD ret = WaitForSingleObject(pinfo->hProcess, INFINITE);
528eaad808eSchristos verbose(VERB_ALGO, "cronaction done");
529eaad808eSchristos if(ret != WAIT_OBJECT_0) {
530eaad808eSchristos return; /* did not end successfully */
531eaad808eSchristos }
532eaad808eSchristos if(!GetExitCodeProcess(pinfo->hProcess, &ret)) {
533eaad808eSchristos log_err("GetExitCodeProcess failed");
534eaad808eSchristos return;
535eaad808eSchristos }
536eaad808eSchristos verbose(VERB_ALGO, "exit code is %d", (int)ret);
537eaad808eSchristos if(ret != 1) {
538eaad808eSchristos if(!WSASetEvent(service_stop_event))
539eaad808eSchristos log_err("Could not WSASetEvent: %s",
540eaad808eSchristos wsa_strerror(WSAGetLastError()));
541eaad808eSchristos }
542eaad808eSchristos }
543eaad808eSchristos
544eaad808eSchristos /** Do the cron action and wait for result exit value */
545eaad808eSchristos static void*
win_do_cron(void * ATTR_UNUSED (arg))546eaad808eSchristos win_do_cron(void* ATTR_UNUSED(arg))
547eaad808eSchristos {
548eaad808eSchristos int mynum=65;
549eaad808eSchristos char* cronaction;
550eaad808eSchristos log_thread_set(&mynum);
551eaad808eSchristos cronaction = lookup_reg_str("Software\\Unbound", "CronAction");
552eaad808eSchristos if(cronaction && strlen(cronaction)>0) {
553eaad808eSchristos STARTUPINFO sinfo;
554eaad808eSchristos PROCESS_INFORMATION pinfo;
555eaad808eSchristos memset(&pinfo, 0, sizeof(pinfo));
556eaad808eSchristos memset(&sinfo, 0, sizeof(sinfo));
557eaad808eSchristos sinfo.cb = sizeof(sinfo);
558eaad808eSchristos verbose(VERB_ALGO, "cronaction: %s", cronaction);
559eaad808eSchristos if(!CreateProcess(NULL, cronaction, NULL, NULL, 0,
560eaad808eSchristos CREATE_NO_WINDOW, NULL, NULL, &sinfo, &pinfo))
561eaad808eSchristos log_err("CreateProcess error");
562eaad808eSchristos else {
563eaad808eSchristos waitforit(&pinfo);
564eaad808eSchristos CloseHandle(pinfo.hProcess);
565eaad808eSchristos CloseHandle(pinfo.hThread);
566eaad808eSchristos }
567eaad808eSchristos }
568eaad808eSchristos free(cronaction);
569eaad808eSchristos /* stop self */
570eaad808eSchristos CloseHandle(cron_thread);
571eaad808eSchristos cron_thread = NULL;
572eaad808eSchristos return NULL;
573eaad808eSchristos }
574eaad808eSchristos
575eaad808eSchristos /** Set the timer for cron for the next wake up */
576eaad808eSchristos static void
set_cron_timer(void)577762909a6Schristos set_cron_timer(void)
578eaad808eSchristos {
579eaad808eSchristos struct timeval tv;
580eaad808eSchristos int crontime;
581eaad808eSchristos if(cron_was_quick == 0) {
582eaad808eSchristos cron_was_quick = 1;
583eaad808eSchristos crontime = 3600; /* first update some time after boot */
584eaad808eSchristos } else {
585eaad808eSchristos crontime = lookup_reg_int("Software\\Unbound", "CronTime");
586eaad808eSchristos if(crontime == 0) crontime = 60*60*24; /* 24 hours */
587eaad808eSchristos }
588eaad808eSchristos memset(&tv, 0, sizeof(tv));
589eaad808eSchristos tv.tv_sec = (time_t)crontime;
590eaad808eSchristos comm_timer_set(service_cron, &tv);
591eaad808eSchristos }
592eaad808eSchristos
593eaad808eSchristos void
wsvc_cron_cb(void * arg)594eaad808eSchristos wsvc_cron_cb(void* arg)
595eaad808eSchristos {
596eaad808eSchristos struct worker* worker = (struct worker*)arg;
597eaad808eSchristos /* perform cronned operation */
598eaad808eSchristos verbose(VERB_ALGO, "cron timer callback");
599eaad808eSchristos if(cron_thread == NULL) {
600eaad808eSchristos /* create new thread to do it */
601eaad808eSchristos ub_thread_create(&cron_thread, win_do_cron, worker);
602eaad808eSchristos }
603eaad808eSchristos /* reschedule */
604eaad808eSchristos set_cron_timer();
605eaad808eSchristos }
606eaad808eSchristos
wsvc_setup_worker(struct worker * worker)607eaad808eSchristos void wsvc_setup_worker(struct worker* worker)
608eaad808eSchristos {
609eaad808eSchristos /* if not started with -w service, do nothing */
610eaad808eSchristos if(!service_stop_event)
611eaad808eSchristos return;
612eaad808eSchristos if(!(service_stop_ev = ub_winsock_register_wsaevent(
613eaad808eSchristos comm_base_internal(worker->base), service_stop_event,
614eaad808eSchristos &worker_win_stop_cb, worker))) {
615eaad808eSchristos fatal_exit("could not register wsaevent");
616eaad808eSchristos return;
617eaad808eSchristos }
618eaad808eSchristos if(!service_cron) {
619eaad808eSchristos service_cron = comm_timer_create(worker->base,
620eaad808eSchristos wsvc_cron_cb, worker);
621eaad808eSchristos if(!service_cron)
622eaad808eSchristos fatal_exit("could not create cron timer");
623eaad808eSchristos set_cron_timer();
624eaad808eSchristos }
625eaad808eSchristos }
626eaad808eSchristos
wsvc_desetup_worker(struct worker * ATTR_UNUSED (worker))627eaad808eSchristos void wsvc_desetup_worker(struct worker* ATTR_UNUSED(worker))
628eaad808eSchristos {
629eaad808eSchristos comm_timer_delete(service_cron);
630eaad808eSchristos service_cron = NULL;
631eaad808eSchristos }
632