aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/winrc/win_svc.c
diff options
context:
space:
mode:
Diffstat (limited to 'external/unbound/winrc/win_svc.c')
-rw-r--r--external/unbound/winrc/win_svc.c622
1 files changed, 0 insertions, 622 deletions
diff --git a/external/unbound/winrc/win_svc.c b/external/unbound/winrc/win_svc.c
deleted file mode 100644
index b755fb543..000000000
--- a/external/unbound/winrc/win_svc.c
+++ /dev/null
@@ -1,622 +0,0 @@
-/*
- * winrc/win_svc.c - windows services API implementation for unbound
- *
- * Copyright (c) 2009, NLnet Labs. All rights reserved.
- *
- * This software is open source.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * Neither the name of the NLNET LABS nor the names of its contributors may
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * \file
- *
- * This file contains functions to integrate with the windows services API.
- * This means it handles the commandline switches to install and remove
- * the service (via CreateService and DeleteService), it handles
- * the ServiceMain() main service entry point when started as a service,
- * and it handles the Handler[_ex]() to process requests to the service
- * (such as start and stop and status).
- */
-#include "config.h"
-#include "winrc/win_svc.h"
-#include "winrc/w_inst.h"
-#include "daemon/daemon.h"
-#include "daemon/worker.h"
-#include "daemon/remote.h"
-#include "util/config_file.h"
-#include "util/netevent.h"
-#include "util/ub_event.h"
-
-/** global service status */
-static SERVICE_STATUS service_status;
-/** global service status handle */
-static SERVICE_STATUS_HANDLE service_status_handle;
-/** global service stop event */
-static WSAEVENT service_stop_event = NULL;
-/** event struct for stop callbacks */
-static struct ub_event* service_stop_ev = NULL;
-/** if stop even means shutdown or restart */
-static int service_stop_shutdown = 0;
-/** config file to open. global communication to service_main() */
-static char* service_cfgfile = CONFIGFILE;
-/** commandline verbosity. global communication to service_main() */
-static int service_cmdline_verbose = 0;
-/** the cron callback */
-static struct comm_timer* service_cron = NULL;
-/** the cron thread */
-static ub_thread_type cron_thread = NULL;
-/** if cron has already done its quick check */
-static int cron_was_quick = 0;
-
-/**
- * Report current service status to service control manager
- * @param state: current state
- * @param exitcode: error code (when stopped)
- * @param wait: pending operation estimated time in milliseconds.
- */
-static void report_status(DWORD state, DWORD exitcode, DWORD wait)
-{
- static DWORD checkpoint = 1;
- service_status.dwCurrentState = state;
- service_status.dwWin32ExitCode = exitcode;
- service_status.dwWaitHint = wait;
- if(state == SERVICE_START_PENDING)
- service_status.dwControlsAccepted = 0;
- else service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
- if(state == SERVICE_RUNNING || state == SERVICE_STOPPED)
- service_status.dwCheckPoint = 0;
- else service_status.dwCheckPoint = checkpoint++;
- SetServiceStatus(service_status_handle, &service_status);
-}
-
-/**
- * Service control handler. Called by serviceControlManager when a control
- * code is sent to the service (with ControlService).
- * @param ctrl: control code
- */
-static void
-hdlr(DWORD ctrl)
-{
- if(ctrl == SERVICE_CONTROL_STOP) {
- report_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
- service_stop_shutdown = 1;
- /* send signal to stop */
- if(!WSASetEvent(service_stop_event))
- log_err("Could not WSASetEvent: %s",
- wsa_strerror(WSAGetLastError()));
- return;
- } else {
- /* ctrl == SERVICE_CONTROL_INTERROGATE or whatever */
- /* update status */
- report_status(service_status.dwCurrentState, NO_ERROR, 0);
- }
-}
-
-/**
- * report event to system event log
- * For use during startup and shutdown.
- * @param str: the error
- */
-static void
-reportev(const char* str)
-{
- char b[256];
- char e[256];
- HANDLE* s;
- LPCTSTR msg = b;
- /* print quickly to keep GetLastError value */
- wsvc_err2str(e, sizeof(e), str, GetLastError());
- snprintf(b, sizeof(b), "%s: %s", SERVICE_NAME, e);
- s = RegisterEventSource(NULL, SERVICE_NAME);
- if(!s) return;
- ReportEvent(s, /* event log */
- EVENTLOG_ERROR_TYPE, /* event type */
- 0, /* event category */
- MSG_GENERIC_ERR, /* event ID (from gen_msg.mc) */
- NULL, /* user security context */
- 1, /* numstrings */
- 0, /* binary size */
- &msg, /* strings */
- NULL); /* binary data */
- DeregisterEventSource(s);
-}
-
-/**
- * Obtain registry string (if it exists).
- * @param key: key string
- * @param name: name of value to fetch.
- * @return malloced string with the result or NULL if it did not
- * exist on an error (logged) was encountered.
- */
-static char*
-lookup_reg_str(const char* key, const char* name)
-{
- HKEY hk = NULL;
- DWORD type = 0;
- BYTE buf[1024];
- DWORD len = (DWORD)sizeof(buf);
- LONG ret;
- char* result = NULL;
- ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hk);
- if(ret == ERROR_FILE_NOT_FOUND)
- return NULL; /* key does not exist */
- else if(ret != ERROR_SUCCESS) {
- reportev("RegOpenKeyEx failed");
- return NULL;
- }
- ret = RegQueryValueEx(hk, (LPCTSTR)name, 0, &type, buf, &len);
- if(RegCloseKey(hk))
- reportev("RegCloseKey");
- if(ret == ERROR_FILE_NOT_FOUND)
- return NULL; /* name does not exist */
- else if(ret != ERROR_SUCCESS) {
- reportev("RegQueryValueEx failed");
- return NULL;
- }
- if(type == REG_SZ || type == REG_MULTI_SZ || type == REG_EXPAND_SZ) {
- buf[sizeof(buf)-1] = 0;
- buf[sizeof(buf)-2] = 0; /* for multi_sz */
- result = strdup((char*)buf);
- if(!result) reportev("out of memory");
- }
- return result;
-}
-
-/**
- * Obtain registry integer (if it exists).
- * @param key: key string
- * @param name: name of value to fetch.
- * @return integer value (if it exists), or 0 on error.
- */
-static int
-lookup_reg_int(const char* key, const char* name)
-{
- HKEY hk = NULL;
- DWORD type = 0;
- BYTE buf[1024];
- DWORD len = (DWORD)sizeof(buf);
- LONG ret;
- int result = 0;
- ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hk);
- if(ret == ERROR_FILE_NOT_FOUND)
- return 0; /* key does not exist */
- else if(ret != ERROR_SUCCESS) {
- reportev("RegOpenKeyEx failed");
- return 0;
- }
- ret = RegQueryValueEx(hk, (LPCTSTR)name, 0, &type, buf, &len);
- if(RegCloseKey(hk))
- reportev("RegCloseKey");
- if(ret == ERROR_FILE_NOT_FOUND)
- return 0; /* name does not exist */
- else if(ret != ERROR_SUCCESS) {
- reportev("RegQueryValueEx failed");
- return 0;
- }
- if(type == REG_SZ || type == REG_MULTI_SZ || type == REG_EXPAND_SZ) {
- buf[sizeof(buf)-1] = 0;
- buf[sizeof(buf)-2] = 0; /* for multi_sz */
- result = atoi((char*)buf);
- } else if(type == REG_DWORD) {
- DWORD r;
- memmove(&r, buf, sizeof(r));
- result = r;
- }
- return result;
-}
-
-/** wait for unbound-anchor process to finish */
-static void
-waitforubanchor(PROCESS_INFORMATION* pinfo)
-{
- /* we have 5 seconds scheduled for it, usually it will be very fast,
- * with only a UDP message or two (100 msec or so), but the https
- * connections could take some time */
- DWORD count = 7900;
- DWORD ret = WAIT_TIMEOUT;
- /* decrease timer every 1/10 second, we are still starting up */
- while(ret == WAIT_TIMEOUT) {
- ret = WaitForSingleObject(pinfo->hProcess, 100);
- if(count > 4000) count -= 100;
- else count--; /* go slow, it is taking long */
- if(count > 3000)
- report_status(SERVICE_START_PENDING, NO_ERROR, count);
- }
- verbose(VERB_ALGO, "unbound-anchor done");
- if(ret != WAIT_OBJECT_0) {
- return; /* did not end successfully */
- }
- if(!GetExitCodeProcess(pinfo->hProcess, &ret)) {
- log_err("GetExitCodeProcess failed");
- return;
- }
- verbose(VERB_ALGO, "unbound-anchor exit code is %d", (int)ret);
- if(ret != 0) {
- log_info("The root trust anchor has been updated.");
- }
-}
-
-
-/**
- * Perform root anchor update if so configured, by calling that process
- */
-static void
-call_root_update(void)
-{
- char* rootanchor;
- rootanchor = lookup_reg_str("Software\\Unbound", "RootAnchor");
- if(rootanchor && strlen(rootanchor)>0) {
- STARTUPINFO sinfo;
- PROCESS_INFORMATION pinfo;
- memset(&pinfo, 0, sizeof(pinfo));
- memset(&sinfo, 0, sizeof(sinfo));
- sinfo.cb = sizeof(sinfo);
- verbose(VERB_ALGO, "rootanchor: %s", rootanchor);
- report_status(SERVICE_START_PENDING, NO_ERROR, 8000);
- if(!CreateProcess(NULL, rootanchor, NULL, NULL, 0,
- CREATE_NO_WINDOW, NULL, NULL, &sinfo, &pinfo))
- log_err("CreateProcess error for unbound-anchor.exe");
- else {
- waitforubanchor(&pinfo);
- CloseHandle(pinfo.hProcess);
- CloseHandle(pinfo.hThread);
- }
- }
- free(rootanchor);
-}
-
-/**
- * Init service. Keeps calling status pending to tell service control
- * manager that this process is not hanging.
- * @param r: restart, true on restart
- * @param d: daemon returned here.
- * @param c: config file returned here.
- * @return false if failed.
- */
-static int
-service_init(int r, struct daemon** d, struct config_file** c)
-{
- struct config_file* cfg = NULL;
- struct daemon* daemon = NULL;
-
- if(!service_cfgfile) {
- char* newf = lookup_reg_str("Software\\Unbound", "ConfigFile");
- if(newf) service_cfgfile = newf;
- else service_cfgfile = strdup(CONFIGFILE);
- if(!service_cfgfile) fatal_exit("out of memory");
- }
-
- /* create daemon */
- if(r) daemon = *d;
- else daemon = daemon_init();
- if(!daemon) return 0;
- if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2800);
-
- /* read config */
- cfg = config_create();
- if(!cfg) return 0;
- if(!config_read(cfg, service_cfgfile, daemon->chroot)) {
- if(errno != ENOENT) {
- log_err("error in config file");
- return 0;
- }
- log_warn("could not open config file, using defaults");
- }
- if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2600);
-
- verbose(VERB_QUERY, "winservice - apply settings");
- /* apply settings and init */
- verbosity = cfg->verbosity + service_cmdline_verbose;
- w_config_adjust_directory(cfg);
- if(cfg->directory && cfg->directory[0]) {
- char* dir = cfg->directory;
- if(chdir(dir)) {
- log_err("could not chdir to %s: %s",
- dir, strerror(errno));
- if(errno != ENOENT)
- return 0;
- log_warn("could not change directory - continuing");
- } else
- verbose(VERB_QUERY, "chdir to %s", dir);
- }
- log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
- if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2400);
- verbose(VERB_QUERY, "winservice - apply cfg");
- daemon_apply_cfg(daemon, cfg);
-
- if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2300);
- if(!(daemon->rc = daemon_remote_create(cfg))) {
- log_err("could not set up remote-control");
- daemon_delete(daemon);
- config_delete(cfg);
- return 0;
- }
-
- /* open ports */
- /* keep reporting that we are busy starting */
- if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2200);
- verbose(VERB_QUERY, "winservice - open ports");
- if(!daemon_open_shared_ports(daemon)) return 0;
- verbose(VERB_QUERY, "winservice - ports opened");
- if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2000);
-
- *d = daemon;
- *c = cfg;
- return 1;
-}
-
-/**
- * Deinit the service
- */
-static void
-service_deinit(struct daemon* daemon, struct config_file* cfg)
-{
- daemon_cleanup(daemon);
- config_delete(cfg);
- daemon_delete(daemon);
-}
-
-#ifdef DOXYGEN
-#define ATTR_UNUSED(x) x
-#endif
-/**
- * The main function for the service.
- * Called by the services API when starting unbound on windows in background.
- * Arguments could have been present in the string 'path'.
- * @param argc: nr args
- * @param argv: arg text.
- */
-static void
-service_main(DWORD ATTR_UNUSED(argc), LPTSTR* ATTR_UNUSED(argv))
-{
- struct config_file* cfg = NULL;
- struct daemon* daemon = NULL;
-
- service_status_handle = RegisterServiceCtrlHandler(SERVICE_NAME,
- (LPHANDLER_FUNCTION)hdlr);
- if(!service_status_handle) {
- reportev("Could not RegisterServiceCtrlHandler");
- return;
- }
-
- service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
- service_status.dwServiceSpecificExitCode = 0;
-
- /* see if we have root anchor update enabled */
- call_root_update();
-
- /* we are now starting up */
- report_status(SERVICE_START_PENDING, NO_ERROR, 3000);
- if(!service_init(0, &daemon, &cfg)) {
- reportev("Could not service_init");
- report_status(SERVICE_STOPPED, NO_ERROR, 0);
- return;
- }
-
- /* event that gets signalled when we want to quit; it
- * should get registered in the worker-0 waiting loop. */
- service_stop_event = WSACreateEvent();
- if(service_stop_event == WSA_INVALID_EVENT) {
- log_err("WSACreateEvent: %s", wsa_strerror(WSAGetLastError()));
- reportev("Could not WSACreateEvent");
- report_status(SERVICE_STOPPED, NO_ERROR, 0);
- return;
- }
- if(!WSAResetEvent(service_stop_event)) {
- log_err("WSAResetEvent: %s", wsa_strerror(WSAGetLastError()));
- }
-
- /* SetServiceStatus SERVICE_RUNNING;*/
- report_status(SERVICE_RUNNING, NO_ERROR, 0);
- verbose(VERB_QUERY, "winservice - init complete");
-
- /* daemon performs work */
- while(!service_stop_shutdown) {
- daemon_fork(daemon);
- if(!service_stop_shutdown) {
- daemon_cleanup(daemon);
- config_delete(cfg); cfg=NULL;
- if(!service_init(1, &daemon, &cfg)) {
- reportev("Could not service_init");
- report_status(SERVICE_STOPPED, NO_ERROR, 0);
- return;
- }
- }
- }
-
- /* exit */
- verbose(VERB_ALGO, "winservice - cleanup.");
- report_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
- if(service_stop_event) (void)WSACloseEvent(service_stop_event);
- service_deinit(daemon, cfg);
- free(service_cfgfile);
- verbose(VERB_QUERY, "winservice - full stop");
- report_status(SERVICE_STOPPED, NO_ERROR, 0);
-}
-
-/** start the service */
-static void
-service_start(const char* cfgfile, int v, int c)
-{
- SERVICE_TABLE_ENTRY myservices[2] = {
- {SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)service_main},
- {NULL, NULL} };
- verbosity=v;
- if(verbosity >= VERB_QUERY) {
- /* log to file about start sequence */
- fclose(fopen("C:\\unbound.log", "w"));
- log_init("C:\\unbound.log", 0, 0);
- verbose(VERB_QUERY, "open logfile");
- } else log_init(0, 1, 0); /* otherwise, use Application log */
- if(c) {
- service_cfgfile = strdup(cfgfile);
- if(!service_cfgfile) fatal_exit("out of memory");
- } else service_cfgfile = NULL;
- service_cmdline_verbose = v;
- /* this call returns when service has stopped. */
- if(!StartServiceCtrlDispatcher(myservices)) {
- reportev("Could not StartServiceCtrlDispatcher");
- }
-}
-
-void
-wsvc_command_option(const char* wopt, const char* cfgfile, int v, int c)
-{
- if(strcmp(wopt, "install") == 0)
- wsvc_install(stdout, NULL);
- else if(strcmp(wopt, "remove") == 0)
- wsvc_remove(stdout);
- else if(strcmp(wopt, "service") == 0)
- service_start(cfgfile, v, c);
- else if(strcmp(wopt, "start") == 0)
- wsvc_rc_start(stdout);
- else if(strcmp(wopt, "stop") == 0)
- wsvc_rc_stop(stdout);
- else fatal_exit("unknown option: %s", wopt);
- exit(0);
-}
-
-void
-worker_win_stop_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), void* arg)
-{
- struct worker* worker = (struct worker*)arg;
- verbose(VERB_QUERY, "caught stop signal (wsaevent)");
- worker->need_to_exit = 1;
- comm_base_exit(worker->base);
-}
-
-/** wait for cron process to finish */
-static void
-waitforit(PROCESS_INFORMATION* pinfo)
-{
- DWORD ret = WaitForSingleObject(pinfo->hProcess, INFINITE);
- verbose(VERB_ALGO, "cronaction done");
- if(ret != WAIT_OBJECT_0) {
- return; /* did not end successfully */
- }
- if(!GetExitCodeProcess(pinfo->hProcess, &ret)) {
- log_err("GetExitCodeProcess failed");
- return;
- }
- verbose(VERB_ALGO, "exit code is %d", (int)ret);
- if(ret != 1) {
- if(!WSASetEvent(service_stop_event))
- log_err("Could not WSASetEvent: %s",
- wsa_strerror(WSAGetLastError()));
- }
-}
-
-/** Do the cron action and wait for result exit value */
-static void*
-win_do_cron(void* ATTR_UNUSED(arg))
-{
- int mynum=65;
- char* cronaction;
- log_thread_set(&mynum);
- cronaction = lookup_reg_str("Software\\Unbound", "CronAction");
- if(cronaction && strlen(cronaction)>0) {
- STARTUPINFO sinfo;
- PROCESS_INFORMATION pinfo;
- memset(&pinfo, 0, sizeof(pinfo));
- memset(&sinfo, 0, sizeof(sinfo));
- sinfo.cb = sizeof(sinfo);
- verbose(VERB_ALGO, "cronaction: %s", cronaction);
- if(!CreateProcess(NULL, cronaction, NULL, NULL, 0,
- CREATE_NO_WINDOW, NULL, NULL, &sinfo, &pinfo))
- log_err("CreateProcess error");
- else {
- waitforit(&pinfo);
- CloseHandle(pinfo.hProcess);
- CloseHandle(pinfo.hThread);
- }
- }
- free(cronaction);
- /* stop self */
- CloseHandle(cron_thread);
- cron_thread = NULL;
- return NULL;
-}
-
-/** Set the timer for cron for the next wake up */
-static void
-set_cron_timer(void)
-{
- struct timeval tv;
- int crontime;
- if(cron_was_quick == 0) {
- cron_was_quick = 1;
- crontime = 3600; /* first update some time after boot */
- } else {
- crontime = lookup_reg_int("Software\\Unbound", "CronTime");
- if(crontime == 0) crontime = 60*60*24; /* 24 hours */
- }
- memset(&tv, 0, sizeof(tv));
- tv.tv_sec = (time_t)crontime;
- comm_timer_set(service_cron, &tv);
-}
-
-void
-wsvc_cron_cb(void* arg)
-{
- struct worker* worker = (struct worker*)arg;
- /* perform cronned operation */
- verbose(VERB_ALGO, "cron timer callback");
- if(cron_thread == NULL) {
- /* create new thread to do it */
- ub_thread_create(&cron_thread, win_do_cron, worker);
- }
- /* reschedule */
- set_cron_timer();
-}
-
-void wsvc_setup_worker(struct worker* worker)
-{
- /* if not started with -w service, do nothing */
- if(!service_stop_event)
- return;
- if(!(service_stop_ev = ub_winsock_register_wsaevent(
- comm_base_internal(worker->base), service_stop_event,
- &worker_win_stop_cb, worker))) {
- fatal_exit("could not register wsaevent");
- return;
- }
- if(!service_cron) {
- service_cron = comm_timer_create(worker->base,
- wsvc_cron_cb, worker);
- if(!service_cron)
- fatal_exit("could not create cron timer");
- set_cron_timer();
- }
-}
-
-void wsvc_desetup_worker(struct worker* ATTR_UNUSED(worker))
-{
- comm_timer_delete(service_cron);
- service_cron = NULL;
-}