1 /*
2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2002 Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <stdio.h>
23
24 #include <ntp_stdlib.h>
25 #include "syslog.h"
26 #include "ntpd.h"
27 #include "ntservice.h"
28 #include "clockstuff.h"
29 #include "ntp_iocompletionport.h"
30 #include "ntpd-opts.h"
31 #include "isc/win32os.h"
32 #include <ssl_applink.c>
33
34
35 /*
36 * Globals
37 */
38 static SERVICE_STATUS_HANDLE hServiceStatus = 0;
39 static BOOL foreground = FALSE;
40 static BOOL computer_shutting_down = FALSE;
41 static int glb_argc;
42 static char **glb_argv;
43 HANDLE hServDoneEvent = NULL;
44 extern int accept_wildcard_if_for_winnt;
45
46 /*
47 * Forward declarations
48 */
49 extern void uninit_io_completion_port();
50 extern int ntpdmain(int argc, char *argv[]);
51 extern void WINAPI ServiceControl(DWORD dwCtrlCode);
52 extern void ntservice_exit(void);
53
54 #ifdef WRAP_DBG_MALLOC
55 void *wrap_dbg_malloc(size_t s, const char *f, int l);
56 void *wrap_dbg_realloc(void *p, size_t s, const char *f, int l);
57 void wrap_dbg_free(void *p);
58 void wrap_dbg_free_ex(void *p, const char *f, int l);
59 #endif
60
61 void WINAPI
service_main(DWORD argc,LPTSTR * argv)62 service_main(
63 DWORD argc,
64 LPTSTR *argv
65 )
66 {
67 if (argc > 1) {
68 /*
69 * Let command line parameters from the Windows SCM GUI
70 * override the standard parameters from the ImagePath registry key.
71 */
72 glb_argc = argc;
73 glb_argv = argv;
74 }
75
76 ntpdmain(glb_argc, glb_argv);
77 }
78
79
80 /*
81 * This is the entry point for the executable
82 * We can call ntpdmain() explicitly or via StartServiceCtrlDispatcher()
83 * as we need to.
84 */
main(int argc,char ** argv)85 int main(
86 int argc,
87 char ** argv
88 )
89 {
90 int rc;
91 int argc_after_opts;
92 char ** argv_after_opts;
93
94 ssl_applink();
95
96 /* Save the command line parameters */
97 glb_argc = argc;
98 glb_argv = argv;
99
100 /* Under original Windows NT we must not discard the wildcard */
101 /* socket to workaround a bug in NT's getsockname(). */
102 if (isc_win32os_majorversion() <= 4)
103 accept_wildcard_if_for_winnt = TRUE;
104
105 argc_after_opts = argc;
106 argv_after_opts = argv;
107 parse_cmdline_opts(&argc_after_opts, &argv_after_opts);
108
109 if (HAVE_OPT(QUIT)
110 || HAVE_OPT(SAVECONFIGQUIT)
111 || HAVE_OPT(HELP)
112 #ifdef DEBUG
113 || OPT_VALUE_SET_DEBUG_LEVEL != 0
114 #endif
115 || HAVE_OPT(NOFORK))
116 foreground = TRUE;
117
118 if (foreground) /* run in console window */
119 rc = ntpdmain(argc, argv);
120 else {
121 /* Start up as service */
122
123 SERVICE_TABLE_ENTRY dispatchTable[] = {
124 { TEXT(NTP_DISPLAY_NAME), service_main },
125 { NULL, NULL }
126 };
127
128 rc = StartServiceCtrlDispatcher(dispatchTable);
129 if (rc)
130 rc = 0;
131 else {
132 rc = GetLastError();
133 fprintf(stderr,
134 "%s: unable to start as service:\n"
135 "%s\n"
136 "Use -d, -q, -n, -?, --help or "
137 "--saveconfigquit to run "
138 "interactive.\n",
139 argv[0], ntp_strerror(rc));
140 }
141 }
142 return rc;
143 }
144
145
146 /*
147 * Initialize the Service by registering it.
148 */
149 void
ntservice_init(void)150 ntservice_init(void)
151 {
152 char ConsoleTitle[256];
153
154 if (!foreground) {
155 /* Register handler with the SCM */
156 hServiceStatus = RegisterServiceCtrlHandler(NTP_DISPLAY_NAME,
157 ServiceControl);
158 if (!hServiceStatus) {
159 NTReportError(NTP_SERVICE_NAME,
160 "could not register with SCM");
161 exit(1);
162 }
163 UpdateSCM(SERVICE_START_PENDING);
164 } else {
165 snprintf(ConsoleTitle, sizeof(ConsoleTitle),
166 "NTP Version %s", Version);
167 ConsoleTitle[sizeof(ConsoleTitle) - 1] = '\0';
168 SetConsoleTitle(ConsoleTitle);
169 }
170
171 #ifdef _CRTDBG_MAP_ALLOC
172 /* ask the runtime to dump memory leaks at exit */
173 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF
174 | _CRTDBG_LEAK_CHECK_DF /* report on leaks at exit */
175 | _CRTDBG_CHECK_ALWAYS_DF /* Check heap every alloc/dealloc */
176 #ifdef MALLOC_LINT
177 | _CRTDBG_DELAY_FREE_MEM_DF /* Don't actually free memory */
178 #endif
179 );
180 #ifdef DOES_NOT_WORK
181 /*
182 * hart: I haven't seen this work, running ntpd.exe -n from a shell
183 * to both a file and the debugger output window. Docs indicate it
184 * should cause leak report to go to stderr, but it's only seen if
185 * ntpd runs under a debugger (in the debugger's output), even with
186 * this block of code enabled.
187 */
188 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
189 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
190 #endif
191 #endif /* using MS debug C runtime heap, _CRTDBG_MAP_ALLOC */
192
193 atexit( ntservice_exit );
194 }
195
196 void
ntservice_isup(void)197 ntservice_isup(void)
198 {
199 if (!foreground) {
200 /* Register handler with the SCM */
201 if (!hServiceStatus) {
202 NTReportError(NTP_SERVICE_NAME,
203 "could not report to SCM");
204 exit(1);
205 }
206 UpdateSCM(SERVICE_RUNNING);
207 }
208 }
209
210 /*
211 * Routine to check if the service is stopping
212 * because the computer is shutting down
213 */
214 BOOL
ntservice_systemisshuttingdown(void)215 ntservice_systemisshuttingdown(void)
216 {
217 return computer_shutting_down;
218 }
219
220 void
ntservice_exit(void)221 ntservice_exit(void)
222 {
223 uninit_io_completion_port();
224 Sleep( 200 ); //##++
225
226 reset_winnt_time();
227
228 msyslog(LOG_INFO, "ntservice: The Network Time Protocol Service is stopping.");
229
230 if (!foreground) {
231 /* service mode, need to have the service_main routine
232 * register with the service control manager that the
233 * service has stopped running, before exiting
234 */
235 UpdateSCM(SERVICE_STOPPED);
236 }
237 }
238
239 /*
240 * ServiceControl(): Handles requests from the SCM and passes them on
241 * to the service.
242 */
243 void WINAPI
ServiceControl(DWORD dwCtrlCode)244 ServiceControl(
245 DWORD dwCtrlCode
246 )
247 {
248 static const char * const msg_tab[2] = {
249 "explicit stop",
250 "system shutdown"
251 };
252
253 switch (dwCtrlCode) {
254
255 case SERVICE_CONTROL_SHUTDOWN:
256 computer_shutting_down = TRUE;
257 /* fall through to stop case */
258
259 case SERVICE_CONTROL_STOP:
260 if (WaitableExitEventHandle != NULL) {
261 msyslog(LOG_INFO, "SCM requests stop (%s)",
262 msg_tab[!!computer_shutting_down]);
263 UpdateSCM(SERVICE_STOP_PENDING);
264 SetEvent(WaitableExitEventHandle);
265 Sleep(100); //##++
266 break;
267 }
268 msyslog(LOG_ERR, "SCM requests stop (%s), but have no exit event!",
269 msg_tab[!!computer_shutting_down]);
270 /* FALLTHROUGH */
271
272 case SERVICE_CONTROL_PAUSE:
273 case SERVICE_CONTROL_CONTINUE:
274 case SERVICE_CONTROL_INTERROGATE:
275 default:
276 UpdateSCM(SERVICE_RUNNING);
277 break;
278 }
279 }
280
281 /*
282 * Tell the Service Control Manager the state of the service.
283 */
284 void
UpdateSCM(DWORD state)285 UpdateSCM(
286 DWORD state
287 )
288 {
289 static DWORD dwState = SERVICE_STOPPED;
290 static DWORD dwCheck = 0;
291
292 SERVICE_STATUS ss;
293
294 if (hServiceStatus) {
295 if (state)
296 dwState = state;
297
298 ZERO(ss);
299 ss.dwServiceType |= SERVICE_WIN32_OWN_PROCESS;
300 ss.dwCurrentState = dwState;
301 /* ss.dwServiceSpecificExitCode = 0; default by ZERO(ss) */
302 /* ss.dwWin32ExitCode = NO_ERROR; default by ZERO(ss) */
303
304 switch (dwState) {
305 case SERVICE_START_PENDING:
306 ss.dwControlsAccepted = 0;
307 ss.dwWaitHint = 15000;
308 ss.dwCheckPoint = ++dwCheck;
309 break;
310 case SERVICE_STOP_PENDING:
311 ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
312 ss.dwWaitHint = 3000;
313 ss.dwCheckPoint = ++dwCheck;
314 break;
315 default:
316 ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
317 ss.dwWaitHint = 1000;
318 ss.dwCheckPoint = dwCheck = 0;
319 break;
320 }
321
322 SetServiceStatus(hServiceStatus, &ss);
323 }
324 }
325
326 BOOL WINAPI
OnConsoleEvent(DWORD dwCtrlType)327 OnConsoleEvent(
328 DWORD dwCtrlType
329 )
330 {
331 switch (dwCtrlType) {
332 #ifdef DEBUG
333 case CTRL_BREAK_EVENT:
334 if (debug > 0) {
335 debug <<= 1;
336 }
337 else {
338 debug = 1;
339 }
340 if (debug > 8) {
341 debug = 0;
342 }
343 msyslog(LOG_DEBUG, "debug level %d", debug);
344 break;
345 #else
346 case CTRL_BREAK_EVENT:
347 break;
348 #endif
349
350 case CTRL_C_EVENT:
351 case CTRL_CLOSE_EVENT:
352 case CTRL_SHUTDOWN_EVENT:
353 if (WaitableExitEventHandle != NULL) {
354 SetEvent(WaitableExitEventHandle);
355 Sleep(100); //##++
356 }
357 break;
358
359 default :
360 /* pass to next handler */
361 return FALSE;
362 }
363
364 /* we've handled it, no more handlers should be called */
365 return TRUE;
366 }
367
368