1/*	$NetBSD: ntservice.c,v 1.2.6.1 2012/06/05 21:15:50 bouyer Exp $	*/
2
3/*
4 * Copyright (C) 2004, 2006, 2007, 2009, 2011  Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1999-2002  Internet Software Consortium.
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20/* Id: ntservice.c,v 1.16 2011/01/13 08:50:29 tbox Exp  */
21
22#include <config.h>
23#include <stdio.h>
24
25#include <isc/app.h>
26#include <isc/log.h>
27
28#include <named/globals.h>
29#include <named/ntservice.h>
30#include <named/main.h>
31#include <named/server.h>
32
33/* Handle to SCM for updating service status */
34static SERVICE_STATUS_HANDLE hServiceStatus = 0;
35static BOOL foreground = FALSE;
36static char ConsoleTitle[128];
37
38/*
39 * Forward declarations
40 */
41void ServiceControl(DWORD dwCtrlCode);
42void GetArgs(int *, char ***, char ***);
43int main(int, char *[], char *[]); /* From ns_main.c */
44
45/*
46 * Here we change the entry point for the executable to bindmain() from main()
47 * This allows us to invoke as a service or from the command line easily.
48 */
49#pragma comment(linker, "/entry:bindmain")
50
51/*
52 * This is the entry point for the executable
53 * We can now call main() explicitly or via StartServiceCtrlDispatcher()
54 * as we need to.
55 */
56int bindmain()
57{
58	int rc,
59	i = 1;
60
61	int argc;
62	char **envp, **argv;
63
64	/*
65	 * We changed the entry point function, so we must initialize argv,
66	 * etc. ourselves.  Ick.
67	 */
68	GetArgs(&argc, &argv, &envp);
69
70	/* Command line users should put -f in the options. */
71	/* XXXMPA should use isc_commandline_parse() here. */
72	while (argv[i]) {
73		if (!strcmp(argv[i], "-f") ||
74		    !strcmp(argv[i], "-g") ||
75		    !strcmp(argv[i], "-v") ||
76		    !strcmp(argv[i], "-V")) {
77			foreground = TRUE;
78			break;
79		}
80		i++;
81	}
82
83	if (foreground) {
84		/* run in console window */
85		exit(main(argc, argv, envp));
86	} else {
87		/* Start up as service */
88		char *SERVICE_NAME = BIND_SERVICE_NAME;
89
90		SERVICE_TABLE_ENTRY dispatchTable[] = {
91			{ TEXT(SERVICE_NAME), (LPSERVICE_MAIN_FUNCTION)main },
92			{ NULL, NULL }
93		};
94
95		rc = StartServiceCtrlDispatcher(dispatchTable);
96		if (!rc) {
97			fprintf(stderr, "Use -f to run from the command line.\n");
98			exit(GetLastError());
99		}
100	}
101	exit(0);
102}
103
104/*
105 * Initialize the Service by registering it.
106 */
107void
108ntservice_init() {
109	if (!foreground) {
110		/* Register handler with the SCM */
111		hServiceStatus = RegisterServiceCtrlHandler(BIND_SERVICE_NAME,
112					(LPHANDLER_FUNCTION)ServiceControl);
113		if (!hServiceStatus) {
114			ns_main_earlyfatal(
115				"could not register service control handler");
116			UpdateSCM(SERVICE_STOPPED);
117			exit(1);
118		}
119		UpdateSCM(SERVICE_RUNNING);
120	} else {
121		strcpy(ConsoleTitle, "BIND Version ");
122		strcat(ConsoleTitle, VERSION);
123		SetConsoleTitle(ConsoleTitle);
124	}
125}
126
127void
128ntservice_shutdown() {
129	UpdateSCM(SERVICE_STOPPED);
130}
131/*
132 * Routine to check if this is a service or a foreground program
133 */
134BOOL
135ntservice_isservice() {
136	return(!foreground);
137}
138/*
139 * ServiceControl(): Handles requests from the SCM and passes them on
140 * to named.
141 */
142void
143ServiceControl(DWORD dwCtrlCode) {
144	/* Handle the requested control code */
145	switch(dwCtrlCode) {
146	case SERVICE_CONTROL_INTERROGATE:
147		UpdateSCM(0);
148		break;
149
150	case SERVICE_CONTROL_SHUTDOWN:
151	case SERVICE_CONTROL_STOP:
152		ns_server_flushonshutdown(ns_g_server, ISC_TRUE);
153		isc_app_shutdown();
154		UpdateSCM(SERVICE_STOPPED);
155		break;
156	default:
157		break;
158	}
159}
160
161/*
162 * Tell the Service Control Manager the state of the service.
163 */
164void UpdateSCM(DWORD state) {
165	SERVICE_STATUS ss;
166	static DWORD dwState = SERVICE_STOPPED;
167
168	if (hServiceStatus) {
169		if (state)
170			dwState = state;
171
172		memset(&ss, 0, sizeof(SERVICE_STATUS));
173		ss.dwServiceType |= SERVICE_WIN32_OWN_PROCESS;
174		ss.dwCurrentState = dwState;
175		ss.dwControlsAccepted = SERVICE_ACCEPT_STOP |
176					SERVICE_ACCEPT_SHUTDOWN;
177		ss.dwCheckPoint = 0;
178		ss.dwServiceSpecificExitCode = 0;
179		ss.dwWin32ExitCode = NO_ERROR;
180		ss.dwWaitHint = dwState == SERVICE_STOP_PENDING ? 10000 : 1000;
181
182		if (!SetServiceStatus(hServiceStatus, &ss)) {
183			ss.dwCurrentState = SERVICE_STOPPED;
184			SetServiceStatus(hServiceStatus, &ss);
185		}
186	}
187}
188
189/*
190 * C-runtime stuff used to initialize the app and
191 * get argv, argc, envp.
192 */
193
194typedef struct
195{
196	int newmode;
197} _startupinfo;
198
199_CRTIMP void __cdecl __set_app_type(int);
200_CRTIMP void __cdecl __getmainargs(int *, char ***, char ***, int,
201				   _startupinfo *);
202void __cdecl _setargv(void);
203
204#ifdef _M_IX86
205/* Pentium FDIV adjustment */
206extern int _adjust_fdiv;
207extern int * _imp___adjust_fdiv;
208/* Floating point precision */
209extern void _setdefaultprecision();
210#endif
211
212extern int _newmode;		/* malloc new() handler mode */
213extern int _dowildcard;		/* passed to __getmainargs() */
214
215typedef void (__cdecl *_PVFV)(void);
216extern void __cdecl _initterm(_PVFV *, _PVFV *);
217extern _PVFV *__onexitbegin;
218extern _PVFV *__onexitend;
219extern _CRTIMP char **__initenv;
220
221/*
222 * Do the work that mainCRTStartup() would normally do
223 */
224void GetArgs(int *argc, char ***argv, char ***envp)
225{
226	_startupinfo startinfo;
227
228	/*
229	 * Set the app type to Console (check CRT/SRC/INTERNAL.H:
230	 * \#define _CONSOLE_APP 1)
231	 */
232	__set_app_type(1);
233
234	/* Mark this module as an EXE file */
235	__onexitbegin = __onexitend = (_PVFV *)(-1);
236
237	startinfo.newmode = _newmode;
238	__getmainargs(argc, argv, envp, _dowildcard, &startinfo);
239	__initenv = *envp;
240
241#ifdef _M_IX86
242	_adjust_fdiv = * _imp___adjust_fdiv;
243	_setdefaultprecision();
244#endif
245}
246