mirror of
https://github.com/isc-projects/bind9.git
synced 2026-02-28 04:21:07 -05:00
BIND system tests are run in a Cygwin environment. Apparently Cygwin shell sets the SEM_NOGPFAULTERRORBOX bit in its process error mode which is then inherited by all spawned child processes. This bit prevents the Windows Error Reporting dialog from being displayed, which I assume is part of an effort to contain memory handling errors triggered by Cygwin binaries in the Cygwin environment. Unfortunately, this also prevents automatic crash dump creation by Windows Error Reporting and Cygwin itself does not handle memory errors in native Windows processes spawned from a Cygwin shell. Fix by clearing the SEM_NOGPFAULTERRORBOX bit inside named if it is started in a Cygwin environment, thus overriding the Cygwin-set process error mode in order to enable Windows Error Reporting to handle all named crashes.
468 lines
9.6 KiB
C
468 lines
9.6 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* 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/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <io.h>
|
|
#include <process.h>
|
|
#include <fcntl.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <syslog.h>
|
|
|
|
#include <isc/print.h>
|
|
#include <isc/result.h>
|
|
#include <isc/string.h>
|
|
#include <isc/ntpaths.h>
|
|
#include <isc/util.h>
|
|
#include <isc/win32os.h>
|
|
|
|
#include <named/main.h>
|
|
#include <named/log.h>
|
|
#include <named/os.h>
|
|
#include <named/globals.h>
|
|
#include <named/ntservice.h>
|
|
|
|
|
|
static char *lockfile = NULL;
|
|
static char *pidfile = NULL;
|
|
static int devnullfd = -1;
|
|
static int lockfilefd = -1;
|
|
|
|
static BOOL Initialized = FALSE;
|
|
|
|
static char *version_error =
|
|
"named requires Windows 2000 Service Pack 2 or later to run correctly";
|
|
|
|
void
|
|
named_paths_init(void) {
|
|
if (!Initialized)
|
|
isc_ntpaths_init();
|
|
|
|
named_g_conffile = isc_ntpaths_get(NAMED_CONF_PATH);
|
|
named_g_defaultpidfile = isc_ntpaths_get(NAMED_PID_PATH);
|
|
named_g_defaultlockfile = isc_ntpaths_get(NAMED_LOCK_PATH);
|
|
named_g_keyfile = isc_ntpaths_get(RNDC_KEY_PATH);
|
|
named_g_defaultsessionkeyfile = isc_ntpaths_get(SESSION_KEY_PATH);
|
|
named_g_defaultbindkeys = isc_ntpaths_get(BIND_KEYS_PATH);
|
|
named_g_defaultdnstap = NULL;
|
|
|
|
Initialized = TRUE;
|
|
}
|
|
|
|
/*
|
|
* 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) {
|
|
|
|
if ((isc_win32os_versioncheck(4, 0, 0, 0) >= 0) &&
|
|
(isc_win32os_versioncheck(5, 0, 0, 0) < 0))
|
|
return; /* No problem with Version 4.0 */
|
|
if (isc_win32os_versioncheck(5, 0, 2, 0) < 0)
|
|
if (ntservice_isservice())
|
|
NTReportError(progname, version_error);
|
|
else
|
|
fprintf(stderr, "%s\n", version_error);
|
|
}
|
|
|
|
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
|
|
named_os_init(const char *progname) {
|
|
named_paths_init();
|
|
setup_syslog(progname);
|
|
/*
|
|
* XXXMPA. We may need to split ntservice_init() in two and
|
|
* just mark as running in named_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();
|
|
*/
|
|
version_check(progname);
|
|
/*
|
|
* 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(GetErrorMode() & ~SEM_NOGPFAULTERRORBOX);
|
|
}
|
|
}
|
|
|
|
void
|
|
named_os_daemonize(void) {
|
|
/*
|
|
* Try to set stdin, stdout, and stderr to /dev/null, but press
|
|
* on even if it fails.
|
|
*/
|
|
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
|
|
named_os_opendevnull(void) {
|
|
devnullfd = open("NUL", O_RDWR, 0);
|
|
}
|
|
|
|
void
|
|
named_os_closedevnull(void) {
|
|
if (devnullfd != _fileno(stdin) &&
|
|
devnullfd != _fileno(stdout) &&
|
|
devnullfd != _fileno(stderr)) {
|
|
close(devnullfd);
|
|
devnullfd = -1;
|
|
}
|
|
}
|
|
|
|
void
|
|
named_os_chroot(const char *root) {
|
|
if (root != NULL)
|
|
named_main_earlyfatal("chroot(): isn't supported by Win32 API");
|
|
}
|
|
|
|
void
|
|
named_os_inituserinfo(const char *username) {
|
|
}
|
|
|
|
void
|
|
named_os_changeuser(void) {
|
|
}
|
|
|
|
unsigned int
|
|
ns_os_uid(void) {
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
named_os_adjustnofile(void) {
|
|
}
|
|
|
|
void
|
|
named_os_minprivs(void) {
|
|
}
|
|
|
|
static int
|
|
safe_open(const char *filename, int mode, bool append) {
|
|
int fd;
|
|
struct stat sb;
|
|
|
|
if (stat(filename, &sb) == -1) {
|
|
if (errno != ENOENT)
|
|
return (-1);
|
|
} else if ((sb.st_mode & S_IFREG) == 0)
|
|
return (-1);
|
|
|
|
if (append)
|
|
fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, mode);
|
|
else {
|
|
(void)unlink(filename);
|
|
fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, mode);
|
|
}
|
|
return (fd);
|
|
}
|
|
|
|
static void
|
|
cleanup_pidfile(void) {
|
|
if (pidfile != NULL) {
|
|
(void)unlink(pidfile);
|
|
free(pidfile);
|
|
}
|
|
pidfile = NULL;
|
|
}
|
|
|
|
static void
|
|
cleanup_lockfile(void) {
|
|
if (lockfilefd != -1) {
|
|
close(lockfilefd);
|
|
lockfilefd = -1;
|
|
}
|
|
|
|
if (lockfile != NULL) {
|
|
int n = unlink(lockfile);
|
|
if (n == -1 && errno != ENOENT)
|
|
named_main_earlywarning("unlink '%s': failed",
|
|
lockfile);
|
|
free(lockfile);
|
|
lockfile = NULL;
|
|
}
|
|
}
|
|
|
|
FILE *
|
|
named_os_openfile(const char *filename, int mode, bool switch_user) {
|
|
char strbuf[ISC_STRERRORSIZE];
|
|
FILE *fp;
|
|
int fd;
|
|
|
|
UNUSED(switch_user);
|
|
fd = safe_open(filename, mode, false);
|
|
if (fd < 0) {
|
|
strerror_s(strbuf, sizeof(strbuf), errno);
|
|
named_main_earlywarning("could not open file '%s': %s",
|
|
filename, strbuf);
|
|
return (NULL);
|
|
}
|
|
|
|
fp = fdopen(fd, "w");
|
|
if (fp == NULL) {
|
|
strerror_s(strbuf, sizeof(strbuf), errno);
|
|
named_main_earlywarning("could not fdopen() file '%s': %s",
|
|
filename, strbuf);
|
|
close(fd);
|
|
}
|
|
|
|
return (fp);
|
|
}
|
|
|
|
void
|
|
named_os_writepidfile(const char *filename, bool first_time) {
|
|
FILE *pidlockfile;
|
|
pid_t pid;
|
|
char strbuf[ISC_STRERRORSIZE];
|
|
void (*report)(const char *, ...);
|
|
|
|
/*
|
|
* The caller must ensure any required synchronization.
|
|
*/
|
|
|
|
report = first_time ? named_main_earlyfatal : named_main_earlywarning;
|
|
|
|
cleanup_pidfile();
|
|
|
|
if (filename == NULL)
|
|
return;
|
|
|
|
pidfile = strdup(filename);
|
|
if (pidfile == NULL) {
|
|
strerror_s(strbuf, sizeof(strbuf), errno);
|
|
(*report)("couldn't strdup() '%s': %s", filename, strbuf);
|
|
return;
|
|
}
|
|
|
|
pidlockfile = named_os_openfile(filename,
|
|
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
|
|
false);
|
|
if (pidlockfile == NULL) {
|
|
free(pidfile);
|
|
pidfile = NULL;
|
|
return;
|
|
}
|
|
|
|
pid = getpid();
|
|
|
|
if (fprintf(pidlockfile, "%ld\n", (long)pid) < 0) {
|
|
(*report)("fprintf() to pid file '%s' failed", filename);
|
|
(void)fclose(pidlockfile);
|
|
cleanup_pidfile();
|
|
return;
|
|
}
|
|
if (fflush(pidlockfile) == EOF) {
|
|
(*report)("fflush() to pid file '%s' failed", filename);
|
|
(void)fclose(pidlockfile);
|
|
cleanup_pidfile();
|
|
return;
|
|
}
|
|
(void)fclose(pidlockfile);
|
|
}
|
|
|
|
bool
|
|
named_os_issingleton(const char *filename) {
|
|
char strbuf[ISC_STRERRORSIZE];
|
|
OVERLAPPED o;
|
|
|
|
if (lockfilefd != -1)
|
|
return (true);
|
|
|
|
if (strcasecmp(filename, "none") == 0)
|
|
return (true);
|
|
|
|
lockfile = strdup(filename);
|
|
if (lockfile == NULL) {
|
|
strerror_s(strbuf, sizeof(strbuf), errno);
|
|
named_main_earlyfatal("couldn't allocate memory for '%s': %s",
|
|
filename, strbuf);
|
|
}
|
|
|
|
/*
|
|
* named_os_openfile() uses safeopen() which removes any existing
|
|
* files. We can't use that here.
|
|
*/
|
|
lockfilefd = open(filename, O_WRONLY | O_CREAT,
|
|
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
if (lockfilefd == -1) {
|
|
cleanup_lockfile();
|
|
return (false);
|
|
}
|
|
|
|
memset(&o, 0, sizeof(o));
|
|
/* Expect ERROR_LOCK_VIOLATION if already locked */
|
|
if (!LockFileEx((HANDLE) _get_osfhandle(lockfilefd),
|
|
LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
|
|
0, 0, 1, &o)) {
|
|
cleanup_lockfile();
|
|
return (false);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
|
|
void
|
|
named_os_shutdown(void) {
|
|
closelog();
|
|
cleanup_pidfile();
|
|
|
|
if (lockfilefd != -1) {
|
|
(void) UnlockFile((HANDLE) _get_osfhandle(lockfilefd),
|
|
0, 0, 0, 1);
|
|
}
|
|
cleanup_lockfile();
|
|
|
|
ntservice_shutdown(); /* This MUST be the last thing done */
|
|
}
|
|
|
|
isc_result_t
|
|
named_os_gethostname(char *buf, size_t len) {
|
|
int n;
|
|
|
|
n = gethostname(buf, (int)len);
|
|
return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
|
|
}
|
|
|
|
void
|
|
named_os_shutdownmsg(char *command, isc_buffer_t *text) {
|
|
UNUSED(command);
|
|
UNUSED(text);
|
|
}
|
|
|
|
void
|
|
named_os_tzset(void) {
|
|
#ifdef HAVE_TZSET
|
|
tzset();
|
|
#endif
|
|
}
|
|
|
|
void
|
|
named_os_started(void) {
|
|
ntservice_init();
|
|
}
|
|
|
|
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 *
|
|
named_os_uname(void) {
|
|
if (unamep == NULL)
|
|
getuname();
|
|
return (unamep);
|
|
}
|