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