xref: /netbsd/external/bsd/unbound/dist/winrc/win_svc.c (revision d6959bcf)
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