diff options
Diffstat (limited to '')
-rw-r--r-- | bin/named/win32/os.c | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/bin/named/win32/os.c b/bin/named/win32/os.c new file mode 100644 index 0000000..f89c50d --- /dev/null +++ b/bin/named/win32/os.c @@ -0,0 +1,460 @@ +/* + * 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 <config.h> +#include <stdarg.h> +#include <stdbool.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 <stdio.h> +#include <stdlib.h> +#include <syslog.h> + +#include <isc/print.h> +#include <isc/result.h> +#include <isc/strerror.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 +ns_paths_init(void) { + if (!Initialized) + isc_ntpaths_init(); + + 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); + ns_g_defaultlockfile = isc_ntpaths_get(NAMED_LOCK_PATH); + ns_g_keyfile = isc_ntpaths_get(RNDC_KEY_PATH); + ns_g_defaultsessionkeyfile = isc_ntpaths_get(SESSION_KEY_PATH); + ns_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 +ns_os_init(const char *progname) { + ns_paths_init(); + setup_syslog(progname); + /* + * 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(); + */ + version_check(progname); +} + +void +ns_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 +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; + } +} + +void +ns_os_chroot(const char *root) { + if (root != NULL) + ns_main_earlyfatal("chroot(): isn't supported by Win32 API"); +} + +void +ns_os_inituserinfo(const char *username) { +} + +void +ns_os_changeuser(void) { +} + +unsigned int +ns_os_uid(void) { + return (0); +} + +void +ns_os_adjustnofile(void) { +} + +void +ns_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) + ns_main_earlywarning("unlink '%s': failed", lockfile); + free(lockfile); + lockfile = NULL; + } +} + +FILE * +ns_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) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + ns_main_earlywarning("could not open file '%s': %s", + filename, strbuf); + return (NULL); + } + + fp = fdopen(fd, "w"); + if (fp == NULL) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + ns_main_earlywarning("could not fdopen() file '%s': %s", + filename, strbuf); + close(fd); + } + + return (fp); +} + +void +ns_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 ? ns_main_earlyfatal : ns_main_earlywarning; + + cleanup_pidfile(); + + if (filename == NULL) + return; + + pidfile = strdup(filename); + if (pidfile == NULL) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + (*report)("couldn't strdup() '%s': %s", filename, strbuf); + return; + } + + pidlockfile = ns_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 +ns_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) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + ns_main_earlyfatal("couldn't allocate memory for '%s': %s", + filename, strbuf); + } + + /* + * ns_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 +ns_os_shutdown(void) { + closelog(); + cleanup_pidfile(); + + if (lockfilefd != -1) { + (void) UnlockFile((HANDLE) _get_osfhandle(lockfilefd), + 0, 0, 0, 1); + close(lockfilefd); + lockfilefd = -1; + } + ntservice_shutdown(); /* This MUST be the last thing done */ +} + +isc_result_t +ns_os_gethostname(char *buf, size_t len) { + int n; + + n = gethostname(buf, (int)len); + return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE); +} + +void +ns_os_shutdownmsg(char *command, isc_buffer_t *text) { + UNUSED(command); + UNUSED(text); +} + +void +ns_os_tzset(void) { +#ifdef HAVE_TZSET + tzset(); +#endif +} + +void +ns_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 * +ns_os_uname(void) { + if (unamep == NULL) + getuname(); + return (unamep); +} |