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