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