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 config_auto_slab_values(cfg); 332 } 333 if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2600); 334 335 verbose(VERB_QUERY, "winservice - apply settings"); 336 /* apply settings and init */ 337 verbosity = cfg->verbosity + service_cmdline_verbose; 338 w_config_adjust_directory(cfg); 339 if(cfg->directory && cfg->directory[0]) { 340 char* dir = cfg->directory; 341 if(chdir(dir)) { 342 log_err("could not chdir to %s: %s", 343 dir, strerror(errno)); 344 if(errno != ENOENT) 345 return 0; 346 log_warn("could not change directory - continuing"); 347 } else 348 verbose(VERB_QUERY, "chdir to %s", dir); 349 } 350 log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir); 351 if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2400); 352 verbose(VERB_QUERY, "winservice - apply cfg"); 353 daemon_apply_cfg(daemon, cfg); 354 355 if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2300); 356 if(!r) { 357 if(!daemon_privileged(daemon)) 358 fatal_exit("could not do privileged setup"); 359 } 360 if(!(daemon->rc = daemon_remote_create(cfg))) { 361 log_err("could not set up remote-control"); 362 daemon_delete(daemon); 363 config_delete(cfg); 364 return 0; 365 } 366 if(cfg->ssl_service_key && cfg->ssl_service_key[0]) { 367 if(!(daemon->listen_dot_sslctx = listen_sslctx_create( 368 cfg->ssl_service_key, cfg->ssl_service_pem, NULL, 369 cfg->tls_ciphers, cfg->tls_ciphersuites, 370 (cfg->tls_session_ticket_keys.first && 371 cfg->tls_session_ticket_keys.first->str[0] != 0), 372 1, 0))) { 373 fatal_exit("could not set up listen SSL_CTX"); 374 } 375 #ifdef HAVE_NGHTTP2_NGHTTP2_H 376 if(cfg_has_https(cfg)) { 377 if(!(daemon->listen_doh_sslctx = listen_sslctx_create( 378 cfg->ssl_service_key, cfg->ssl_service_pem, NULL, 379 cfg->tls_ciphers, cfg->tls_ciphersuites, 380 (cfg->tls_session_ticket_keys.first && 381 cfg->tls_session_ticket_keys.first->str[0] != 0), 382 0, 1))) { 383 fatal_exit("could not set up listen doh SSL_CTX"); 384 } 385 } 386 #endif 387 #ifdef HAVE_NGTCP2 388 if(cfg_has_quic(cfg)) { 389 if(!(daemon->listen_quic_sslctx = quic_sslctx_create( 390 cfg->ssl_service_key, cfg->ssl_service_pem, NULL))) { 391 fatal_exit("could not set up quic SSL_CTX"); 392 } 393 } 394 #endif /* HAVE_NGTCP2 */ 395 } 396 if(!(daemon->connect_dot_sslctx = connect_sslctx_create(NULL, NULL, 397 cfg->tls_cert_bundle, cfg->tls_win_cert))) 398 fatal_exit("could not set up connect SSL_CTX"); 399 400 /* open ports */ 401 /* keep reporting that we are busy starting */ 402 if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2200); 403 verbose(VERB_QUERY, "winservice - open ports"); 404 if(!daemon_open_shared_ports(daemon)) return 0; 405 verbose(VERB_QUERY, "winservice - ports opened"); 406 if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2000); 407 408 *d = daemon; 409 *c = cfg; 410 return 1; 411 } 412 413 /** 414 * Deinit the service 415 */ 416 static void 417 service_deinit(struct daemon* daemon, struct config_file* cfg) 418 { 419 daemon_cleanup(daemon); 420 config_delete(cfg); 421 daemon_delete(daemon); 422 } 423 424 #ifdef DOXYGEN 425 #define ATTR_UNUSED(x) x 426 #endif 427 /** 428 * The main function for the service. 429 * Called by the services API when starting unbound on windows in background. 430 * Arguments could have been present in the string 'path'. 431 * @param argc: nr args 432 * @param argv: arg text. 433 */ 434 static void 435 service_main(DWORD ATTR_UNUSED(argc), LPTSTR* ATTR_UNUSED(argv)) 436 { 437 struct config_file* cfg = NULL; 438 struct daemon* daemon = NULL; 439 440 service_status_handle = RegisterServiceCtrlHandler(SERVICE_NAME, 441 (LPHANDLER_FUNCTION)hdlr); 442 if(!service_status_handle) { 443 reportev("Could not RegisterServiceCtrlHandler"); 444 return; 445 } 446 447 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 448 service_status.dwServiceSpecificExitCode = 0; 449 450 /* see if we have root anchor update enabled */ 451 call_root_update(); 452 453 /* we are now starting up */ 454 report_status(SERVICE_START_PENDING, NO_ERROR, 3000); 455 if(!service_init(0, &daemon, &cfg)) { 456 reportev("Could not service_init"); 457 report_status(SERVICE_STOPPED, NO_ERROR, 0); 458 return; 459 } 460 461 /* event that gets signalled when we want to quit; it 462 * should get registered in the worker-0 waiting loop. */ 463 service_stop_event = WSACreateEvent(); 464 if(service_stop_event == WSA_INVALID_EVENT) { 465 log_err("WSACreateEvent: %s", wsa_strerror(WSAGetLastError())); 466 reportev("Could not WSACreateEvent"); 467 report_status(SERVICE_STOPPED, NO_ERROR, 0); 468 return; 469 } 470 if(!WSAResetEvent(service_stop_event)) { 471 log_err("WSAResetEvent: %s", wsa_strerror(WSAGetLastError())); 472 } 473 474 /* SetServiceStatus SERVICE_RUNNING;*/ 475 report_status(SERVICE_RUNNING, NO_ERROR, 0); 476 verbose(VERB_QUERY, "winservice - init complete"); 477 478 /* daemon performs work */ 479 while(!service_stop_shutdown) { 480 daemon_fork(daemon); 481 if(!service_stop_shutdown) { 482 daemon_cleanup(daemon); 483 config_delete(cfg); cfg=NULL; 484 if(!service_init(1, &daemon, &cfg)) { 485 reportev("Could not service_init"); 486 report_status(SERVICE_STOPPED, NO_ERROR, 0); 487 return; 488 } 489 } 490 } 491 492 /* exit */ 493 verbose(VERB_ALGO, "winservice - cleanup."); 494 report_status(SERVICE_STOP_PENDING, NO_ERROR, 0); 495 if(service_stop_event) (void)WSACloseEvent(service_stop_event); 496 service_deinit(daemon, cfg); 497 free(service_cfgfile); 498 verbose(VERB_QUERY, "winservice - full stop"); 499 report_status(SERVICE_STOPPED, NO_ERROR, 0); 500 } 501 502 /** start the service */ 503 static void 504 service_start(const char* cfgfile, int v, int c) 505 { 506 SERVICE_TABLE_ENTRY myservices[2] = { 507 {SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)service_main}, 508 {NULL, NULL} }; 509 verbosity=v; 510 if(verbosity >= VERB_QUERY) { 511 /* log to file about start sequence */ 512 fclose(fopen("C:\\unbound.log", "w")); 513 log_init("C:\\unbound.log", 0, 0); 514 verbose(VERB_QUERY, "open logfile"); 515 } else log_init(0, 1, 0); /* otherwise, use Application log */ 516 if(c) { 517 service_cfgfile = strdup(cfgfile); 518 if(!service_cfgfile) fatal_exit("out of memory"); 519 } else service_cfgfile = NULL; 520 service_cmdline_verbose = v; 521 /* this call returns when service has stopped. */ 522 if(!StartServiceCtrlDispatcher(myservices)) { 523 reportev("Could not StartServiceCtrlDispatcher"); 524 } 525 } 526 527 void 528 wsvc_command_option(const char* wopt, const char* cfgfile, int v, int c) 529 { 530 if(strcmp(wopt, "install") == 0) 531 wsvc_install(stdout, NULL); 532 else if(strcmp(wopt, "remove") == 0) 533 wsvc_remove(stdout); 534 else if(strcmp(wopt, "service") == 0) 535 service_start(cfgfile, v, c); 536 else if(strcmp(wopt, "start") == 0) 537 wsvc_rc_start(stdout); 538 else if(strcmp(wopt, "stop") == 0) 539 wsvc_rc_stop(stdout); 540 else fatal_exit("unknown option: %s", wopt); 541 exit(0); 542 } 543 544 void 545 worker_win_stop_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), void* arg) 546 { 547 struct worker* worker = (struct worker*)arg; 548 verbose(VERB_QUERY, "caught stop signal (wsaevent)"); 549 worker->need_to_exit = 1; 550 comm_base_exit(worker->base); 551 } 552 553 /** wait for cron process to finish */ 554 static void 555 waitforit(PROCESS_INFORMATION* pinfo) 556 { 557 DWORD ret = WaitForSingleObject(pinfo->hProcess, INFINITE); 558 verbose(VERB_ALGO, "cronaction done"); 559 if(ret != WAIT_OBJECT_0) { 560 return; /* did not end successfully */ 561 } 562 if(!GetExitCodeProcess(pinfo->hProcess, &ret)) { 563 log_err("GetExitCodeProcess failed"); 564 return; 565 } 566 verbose(VERB_ALGO, "exit code is %d", (int)ret); 567 if(ret != 1) { 568 if(!WSASetEvent(service_stop_event)) 569 log_err("Could not WSASetEvent: %s", 570 wsa_strerror(WSAGetLastError())); 571 } 572 } 573 574 /** Do the cron action and wait for result exit value */ 575 static void* 576 win_do_cron(void* ATTR_UNUSED(arg)) 577 { 578 int mynum=65; 579 char* cronaction; 580 log_thread_set(&mynum); 581 cronaction = lookup_reg_str("Software\\Unbound", "CronAction"); 582 if(cronaction && strlen(cronaction)>0) { 583 STARTUPINFO sinfo; 584 PROCESS_INFORMATION pinfo; 585 memset(&pinfo, 0, sizeof(pinfo)); 586 memset(&sinfo, 0, sizeof(sinfo)); 587 sinfo.cb = sizeof(sinfo); 588 verbose(VERB_ALGO, "cronaction: %s", cronaction); 589 if(!CreateProcess(NULL, cronaction, NULL, NULL, 0, 590 CREATE_NO_WINDOW, NULL, NULL, &sinfo, &pinfo)) 591 log_err("CreateProcess error"); 592 else { 593 waitforit(&pinfo); 594 CloseHandle(pinfo.hProcess); 595 CloseHandle(pinfo.hThread); 596 } 597 } 598 free(cronaction); 599 /* stop self */ 600 CloseHandle(cron_thread); 601 cron_thread = NULL; 602 return NULL; 603 } 604 605 /** Set the timer for cron for the next wake up */ 606 static void 607 set_cron_timer(void) 608 { 609 struct timeval tv; 610 int crontime; 611 if(cron_was_quick == 0) { 612 cron_was_quick = 1; 613 crontime = 3600; /* first update some time after boot */ 614 } else { 615 crontime = lookup_reg_int("Software\\Unbound", "CronTime"); 616 if(crontime == 0) crontime = 60*60*24; /* 24 hours */ 617 } 618 memset(&tv, 0, sizeof(tv)); 619 tv.tv_sec = (time_t)crontime; 620 comm_timer_set(service_cron, &tv); 621 } 622 623 void 624 wsvc_cron_cb(void* arg) 625 { 626 struct worker* worker = (struct worker*)arg; 627 /* perform cronned operation */ 628 verbose(VERB_ALGO, "cron timer callback"); 629 if(cron_thread == NULL) { 630 /* create new thread to do it */ 631 ub_thread_create(&cron_thread, win_do_cron, worker); 632 } 633 /* reschedule */ 634 set_cron_timer(); 635 } 636 637 void wsvc_setup_worker(struct worker* worker) 638 { 639 /* if not started with -w service, do nothing */ 640 if(!service_stop_event) 641 return; 642 if(!(service_stop_ev = ub_winsock_register_wsaevent( 643 comm_base_internal(worker->base), service_stop_event, 644 &worker_win_stop_cb, worker))) { 645 fatal_exit("could not register wsaevent"); 646 return; 647 } 648 if(!service_cron) { 649 service_cron = comm_timer_create(worker->base, 650 wsvc_cron_cb, worker); 651 if(!service_cron) 652 fatal_exit("could not create cron timer"); 653 set_cron_timer(); 654 } 655 } 656 657 void wsvc_desetup_worker(struct worker* ATTR_UNUSED(worker)) 658 { 659 comm_timer_delete(service_cron); 660 service_cron = NULL; 661 } 662