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