bind9/bin/named/win32/os.c
Michał Kępień 3d4b17806f Prevent Cygwin from concealing non-abort() crashes
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.
2019-09-26 10:34:01 +02:00

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);
}