os.c 9.69 KB
Newer Older
Danny Mayer's avatar
Danny Mayer committed
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
Danny Mayer's avatar
Danny Mayer committed
3
 *
4 5 6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 8 9
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
Danny Mayer's avatar
Danny Mayer committed
10 11 12 13
 */

#include <config.h>
#include <stdarg.h>
14
#include <stdbool.h>
Danny Mayer's avatar
Danny Mayer committed
15

Evan Hunt's avatar
Evan Hunt committed
16
#include <sys/types.h>
Danny Mayer's avatar
Danny Mayer committed
17 18 19 20 21 22 23 24 25 26 27 28 29
#include <sys/stat.h>

#include <ctype.h>
#include <errno.h>
#include <io.h>
#include <process.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>

#include <isc/print.h>
#include <isc/result.h>
30
#include <isc/strerror.h>
Danny Mayer's avatar
Danny Mayer committed
31 32
#include <isc/string.h>
#include <isc/ntpaths.h>
33
#include <isc/util.h>
34
#include <isc/win32os.h>
Danny Mayer's avatar
Danny Mayer committed
35 36

#include <named/main.h>
37
#include <named/log.h>
Danny Mayer's avatar
Danny Mayer committed
38 39 40 41 42
#include <named/os.h>
#include <named/globals.h>
#include <named/ntservice.h>


43
static char *lockfile = NULL;
Danny Mayer's avatar
Danny Mayer committed
44
static char *pidfile = NULL;
45
static int devnullfd = -1;
46
static int lockfilefd = -1;
Danny Mayer's avatar
Danny Mayer committed
47 48 49

static BOOL Initialized = FALSE;

Automatic Updater's avatar
Automatic Updater committed
50
static char *version_error =
51 52
	"named requires Windows 2000 Service Pack 2 or later to run correctly";

Danny Mayer's avatar
Danny Mayer committed
53
void
54
ns_paths_init(void) {
55
	if (!Initialized)
Danny Mayer's avatar
Danny Mayer committed
56
		isc_ntpaths_init();
57

Danny Mayer's avatar
Danny Mayer committed
58 59 60 61 62
	lwresd_g_conffile = isc_ntpaths_get(LWRES_CONF_PATH);
	lwresd_g_resolvconffile = isc_ntpaths_get(RESOLV_CONF_PATH);
	ns_g_conffile = isc_ntpaths_get(NAMED_CONF_PATH);
	ns_g_defaultpidfile = isc_ntpaths_get(NAMED_PID_PATH);
	lwresd_g_defaultpidfile = isc_ntpaths_get(LWRESD_PID_PATH);
63
	ns_g_defaultlockfile = isc_ntpaths_get(NAMED_LOCK_PATH);
64
	ns_g_keyfile = isc_ntpaths_get(RNDC_KEY_PATH);
65
	ns_g_defaultsessionkeyfile = isc_ntpaths_get(SESSION_KEY_PATH);
Danny Mayer's avatar
Danny Mayer committed
66 67 68 69

	Initialized = TRUE;
}

70 71 72 73 74 75 76 77
/*
 * Due to Knowledge base article Q263823 we need to make sure that
 * Windows 2000 systems have Service Pack 2 or later installed and
 * warn when it isn't.
 */
static void
version_check(const char *progname) {

78 79
	if ((isc_win32os_versioncheck(4, 0, 0, 0) >= 0) &&
	    (isc_win32os_versioncheck(5, 0, 0, 0) < 0))
80
		return;	/* No problem with Version 4.0 */
81
	if (isc_win32os_versioncheck(5, 0, 2, 0) < 0)
82 83
		if (ntservice_isservice())
			NTReportError(progname, version_error);
Automatic Updater's avatar
Automatic Updater committed
84
		else
85 86
			fprintf(stderr, "%s\n", version_error);
}
Danny Mayer's avatar
Danny Mayer committed
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

static void
setup_syslog(const char *progname) {
	int options;

	options = LOG_PID;
#ifdef LOG_NDELAY
	options |= LOG_NDELAY;
#endif

	openlog(progname, options, LOG_DAEMON);
}

void
ns_os_init(const char *progname) {
	ns_paths_init();
	setup_syslog(progname);
104 105 106 107 108 109 110 111 112 113
	/*
	 * XXXMPA. We may need to split ntservice_init() in two and
	 * just mark as running in ns_os_started().  If we do that
	 * this is where the first part of ntservice_init() should be
	 * called from.
	 *
	 * XXX970 Remove comment if no problems by 9.7.0.
	 *
	 * ntservice_init();
	 */
114
	version_check(progname);
115 116 117 118 119 120 121 122 123 124
	/*
	 * If running in a Cygwin environment, clear the SEM_NOGPFAULTERRORBOX
	 * bit in the process error mode to prevent Cygwin from concealing
	 * non-abort() crashes, giving Windows Error Reporting a chance to
	 * handle such crashes.  This is done to ensure all crashes triggered
	 * by system tests can be detected.
	 */
	if (getenv("CYGWIN") != NULL) {
		SetErrorMode(SetErrorMode(0) & ~SEM_NOGPFAULTERRORBOX);
	}
Danny Mayer's avatar
Danny Mayer committed
125 126 127 128 129 130 131 132
}

void
ns_os_daemonize(void) {
	/*
	 * Try to set stdin, stdout, and stderr to /dev/null, but press
	 * on even if it fails.
	 */
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
	if (devnullfd != -1) {
		if (devnullfd != _fileno(stdin)) {
			close(_fileno(stdin));
			(void)_dup2(devnullfd, _fileno(stdin));
		}
		if (devnullfd != _fileno(stdout)) {
			close(_fileno(stdout));
			(void)_dup2(devnullfd, _fileno(stdout));
		}
		if (devnullfd != _fileno(stderr)) {
			close(_fileno(stderr));
			(void)_dup2(devnullfd, _fileno(stderr));
		}
	}
}

void
ns_os_opendevnull(void) {
	devnullfd = open("NUL", O_RDWR, 0);
}

void
ns_os_closedevnull(void) {
	if (devnullfd != _fileno(stdin) &&
	    devnullfd != _fileno(stdout) &&
	    devnullfd != _fileno(stderr)) {
		close(devnullfd);
		devnullfd = -1;
Danny Mayer's avatar
Danny Mayer committed
161 162 163 164 165
	}
}

void
ns_os_chroot(const char *root) {
Mark Andrews's avatar
Mark Andrews committed
166 167
	if (root != NULL)
		ns_main_earlyfatal("chroot(): isn't supported by Win32 API");
Danny Mayer's avatar
Danny Mayer committed
168 169 170 171 172 173 174 175 176 177
}

void
ns_os_inituserinfo(const char *username) {
}

void
ns_os_changeuser(void) {
}

178 179 180 181 182
unsigned int
ns_os_uid(void) {
	return (0);
}

183 184 185 186
void
ns_os_adjustnofile(void) {
}

Danny Mayer's avatar
Danny Mayer committed
187 188 189 190 191
void
ns_os_minprivs(void) {
}

static int
192
safe_open(const char *filename, int mode, bool append) {
Danny Mayer's avatar
Danny Mayer committed
193
	int fd;
Mark Andrews's avatar
Mark Andrews committed
194
	struct stat sb;
Danny Mayer's avatar
Danny Mayer committed
195

Mark Andrews's avatar
Mark Andrews committed
196 197
	if (stat(filename, &sb) == -1) {
		if (errno != ENOENT)
Danny Mayer's avatar
Danny Mayer committed
198
			return (-1);
Mark Andrews's avatar
Mark Andrews committed
199
	} else if ((sb.st_mode & S_IFREG) == 0)
Danny Mayer's avatar
Danny Mayer committed
200 201 202
		return (-1);

	if (append)
203
		fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, mode);
Danny Mayer's avatar
Danny Mayer committed
204 205
	else {
		(void)unlink(filename);
206
		fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, mode);
Danny Mayer's avatar
Danny Mayer committed
207 208 209 210 211 212 213 214 215 216 217 218 219
	}
	return (fd);
}

static void
cleanup_pidfile(void) {
	if (pidfile != NULL) {
		(void)unlink(pidfile);
		free(pidfile);
	}
	pidfile = NULL;
}

220 221
static void
cleanup_lockfile(void) {
222 223 224
	if (lockfilefd != -1) {
		close(lockfilefd);
		lockfilefd = -1;
225 226 227 228 229 230 231 232 233 234 235
	}

	if (lockfile != NULL) {
		int n = unlink(lockfile);
		if (n == -1 && errno != ENOENT)
			ns_main_earlywarning("unlink '%s': failed", lockfile);
		free(lockfile);
		lockfile = NULL;
	}
}

236
FILE *
237
ns_os_openfile(const char *filename, int mode, bool switch_user) {
238 239 240 241 242
	char strbuf[ISC_STRERRORSIZE];
	FILE *fp;
	int fd;

	UNUSED(switch_user);
243
	fd = safe_open(filename, mode, false);
244 245 246 247
	if (fd < 0) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlywarning("could not open file '%s': %s",
				     filename, strbuf);
248
		return (NULL);
249 250 251
	}

	fp = fdopen(fd, "w");
Evan Hunt's avatar
Evan Hunt committed
252
	if (fp == NULL) {
253 254 255 256 257 258 259 260 261
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlywarning("could not fdopen() file '%s': %s",
				     filename, strbuf);
		close(fd);
	}

	return (fp);
}

Danny Mayer's avatar
Danny Mayer committed
262
void
263
ns_os_writepidfile(const char *filename, bool first_time) {
264
	FILE *pidlockfile;
Danny Mayer's avatar
Danny Mayer committed
265
	pid_t pid;
266
	char strbuf[ISC_STRERRORSIZE];
267
	void (*report)(const char *, ...);
Danny Mayer's avatar
Danny Mayer committed
268 269 270 271 272

	/*
	 * The caller must ensure any required synchronization.
	 */

273 274
	report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;

Danny Mayer's avatar
Danny Mayer committed
275 276
	cleanup_pidfile();

277
	if (filename == NULL)
278
		return;
279

Evan Hunt's avatar
Evan Hunt committed
280
	pidfile = strdup(filename);
281
	if (pidfile == NULL) {
282
		isc__strerror(errno, strbuf, sizeof(strbuf));
283
		(*report)("couldn't strdup() '%s': %s", filename, strbuf);
284
		return;
285
	}
Danny Mayer's avatar
Danny Mayer committed
286

287
	pidlockfile = ns_os_openfile(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
288
				     false);
289
	if (pidlockfile == NULL) {
290 291 292
		free(pidfile);
		pidfile = NULL;
		return;
293
	}
Danny Mayer's avatar
Danny Mayer committed
294

295 296
	pid = getpid();

297
	if (fprintf(pidlockfile, "%ld\n", (long)pid) < 0) {
Mark Andrews's avatar
Mark Andrews committed
298
		(*report)("fprintf() to pid file '%s' failed", filename);
299
		(void)fclose(pidlockfile);
300 301 302
		cleanup_pidfile();
		return;
	}
303
	if (fflush(pidlockfile) == EOF) {
Mark Andrews's avatar
Mark Andrews committed
304
		(*report)("fflush() to pid file '%s' failed", filename);
305
		(void)fclose(pidlockfile);
306 307 308
		cleanup_pidfile();
		return;
	}
309
	(void)fclose(pidlockfile);
Danny Mayer's avatar
Danny Mayer committed
310 311
}

312
bool
313
ns_os_issingleton(const char *filename) {
314
	char strbuf[ISC_STRERRORSIZE];
315 316
	OVERLAPPED o;

317
	if (lockfilefd != -1)
318
		return (true);
319

320
	if (strcasecmp(filename, "none") == 0)
321
		return (true);
322 323 324 325 326 327 328 329

	lockfile = strdup(filename);
	if (lockfile == NULL) {
		isc__strerror(errno, strbuf, sizeof(strbuf));
		ns_main_earlyfatal("couldn't allocate memory for '%s': %s",
				   filename, strbuf);
	}

330 331 332 333
	/*
	 * ns_os_openfile() uses safeopen() which removes any existing
	 * files. We can't use that here.
	 */
334 335 336
	lockfilefd = open(filename, O_WRONLY | O_CREAT,
			  S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
	if (lockfilefd == -1) {
337
		cleanup_lockfile();
338
		return (false);
339
	}
340 341 342

	memset(&o, 0, sizeof(o));
	/* Expect ERROR_LOCK_VIOLATION if already locked */
343
	if (!LockFileEx((HANDLE) _get_osfhandle(lockfilefd),
344 345
			LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
			0, 0, 1, &o)) {
346
		cleanup_lockfile();
347
		return (false);
348 349
	}

350
	return (true);
351 352
}

353

Danny Mayer's avatar
Danny Mayer committed
354 355 356 357
void
ns_os_shutdown(void) {
	closelog();
	cleanup_pidfile();
358

359 360
	if (lockfilefd != -1) {
		(void) UnlockFile((HANDLE) _get_osfhandle(lockfilefd),
361 362
				  0, 0, 0, 1);
	}
363 364
	cleanup_lockfile();

Danny Mayer's avatar
Danny Mayer committed
365 366
	ntservice_shutdown();	/* This MUST be the last thing done */
}
367 368 369

isc_result_t
ns_os_gethostname(char *buf, size_t len) {
Mark Andrews's avatar
Mark Andrews committed
370
	int n;
371

372
	n = gethostname(buf, (int)len);
373
	return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
374
}
375

376 377 378 379 380
void
ns_os_shutdownmsg(char *command, isc_buffer_t *text) {
	UNUSED(command);
	UNUSED(text);
}
381 382 383 384 385 386 387

void
ns_os_tzset(void) {
#ifdef HAVE_TZSET
	tzset();
#endif
}
388 389 390

void
ns_os_started(void) {
391
	ntservice_init();
392
}
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469

static char unamebuf[BUFSIZ];
static char *unamep = NULL;

static void
getuname(void) {
	DWORD fvilen;
	char *fvi;
	VS_FIXEDFILEINFO *ffi;
	UINT ffilen;
	SYSTEM_INFO sysinfo;
	char *arch;

	fvi = NULL;
	fvilen = GetFileVersionInfoSize("kernel32.dll", 0);
	if (fvilen == 0) {
		goto err;
	}
	fvi = (char *)malloc(fvilen);
	if (fvi == NULL) {
		goto err;
	}
	memset(fvi, 0, fvilen);
	if (GetFileVersionInfo("kernel32.dll", 0, fvilen, fvi) == 0) {
		goto err;
	}
	ffi = NULL;
	ffilen = 0;
	if ((VerQueryValue(fvi, "\\", &ffi, &ffilen) == 0) ||
	    (ffi == NULL) || (ffilen == 0)) {
		goto err;
	}
	memset(&sysinfo, 0, sizeof(sysinfo));
	GetSystemInfo(&sysinfo);
	switch (sysinfo.wProcessorArchitecture) {
	case PROCESSOR_ARCHITECTURE_INTEL:
		arch = "x86";
		break;
	case PROCESSOR_ARCHITECTURE_ARM:
		arch = "arm";
		break;
	case PROCESSOR_ARCHITECTURE_IA64:
		arch = "ia64";
		break;
	case PROCESSOR_ARCHITECTURE_AMD64:
		arch = "x64";
		break;
	default:
		arch = "unknown architecture";
		break;
	}

	snprintf(unamebuf, sizeof(unamebuf),
		 "Windows %d %d build %d %d for %s\n",
		 (ffi->dwProductVersionMS >> 16) & 0xffff,
		 ffi->dwProductVersionMS & 0xffff,
		 (ffi->dwProductVersionLS >> 16) & 0xffff,
		 ffi->dwProductVersionLS & 0xffff,
		 arch);

    err:
	if (fvi != NULL) {
		free(fvi);
	}
	unamep = unamebuf;
}

/*
 * GetVersionEx() returns 6.2 (aka Windows 8.1) since it was obsoleted
 * so we had to switch to the recommended way to get the Windows version.
 */
char *
ns_os_uname(void) {
	if (unamep == NULL)
		getuname();
	return (unamep);
}