//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany /// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Dr. Oreste Costa-Panaia //////////////////////////////////////////////////////////////////////////////// #include #include #include "win-utils.h" #include #include #include #include #include #include #include #include #include "Logger/Logger.h" #include "Basics/files.h" #include "Basics/StringUtils.h" #include "Basics/tri-strings.h" #include "Basics/directories.h" using namespace arangodb::basics; // ............................................................................. // Some global variables which may be required later // ............................................................................. _invalid_parameter_handler oldInvalidHandleHandler; _invalid_parameter_handler newInvalidHandleHandler; // Windows variant for unistd.h's ftruncate() int ftruncate(int fd, long newSize) { int result = _chsize(fd, newSize); return result; } // Windows variant for getpagesize() int getpagesize(void) { static int pageSize = 0; // only define it once if (!pageSize) { // first time, so call the system info function SYSTEM_INFO systemInfo; GetSystemInfo(&systemInfo); pageSize = systemInfo.dwPageSize; } return pageSize; } //////////////////////////////////////////////////////////////////////////////// // Sets up a handler when invalid (win) handles are passed to a windows // function. // This is not of much use since no values can be returned. All we can do // for now is to ignore error and hope it goes away! //////////////////////////////////////////////////////////////////////////////// static void InvalidParameterHandler( const wchar_t* expression, // expression sent to function - NULL const wchar_t* function, // name of function - NULL const wchar_t* file, // file where code resides - NULL unsigned int line, // line within file - NULL uintptr_t pReserved) { // in case microsoft forget something #ifdef ARANGODB_ENABLE_MAINTAINER_MODE std::string exp; std::string func; std::string fileName; UnicodeString uStr; uStr = expression; uStr.toUTF8String(exp); uStr = function; uStr.toUTF8String(func); uStr = file; uStr.toUTF8String(fileName); std::string bt; TRI_GetBacktrace(bt); #endif LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "Invalid handle parameter passed" #ifdef ARANGODB_ENABLE_MAINTAINER_MODE << " Expression: " << exp << " Function: " << func << " File: " << fileName << " Line: " << std::to_string(line) << " Backtrace: " << bt #endif ; } int initializeWindows(const TRI_win_initialize_e initializeWhat, char const* data) { // ............................................................................ // The data is used to transport information from the calling function to here // it may be NULL (and will be in most cases) // ............................................................................ switch (initializeWhat) { case TRI_WIN_INITIAL_SET_DEBUG_FLAG: { _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF) | _CRTDBG_CHECK_ALWAYS_DF); return 0; } // ........................................................................... // Assign a handler for invalid handles // ........................................................................... case TRI_WIN_INITIAL_SET_INVALID_HANLE_HANDLER: { newInvalidHandleHandler = InvalidParameterHandler; oldInvalidHandleHandler = _set_invalid_parameter_handler(newInvalidHandleHandler); return 0; } case TRI_WIN_INITIAL_SET_MAX_STD_IO: { int* newMax = (int*)(data); int result = _setmaxstdio(*newMax); if (result != *newMax) { return -1; } return 0; } case TRI_WIN_INITIAL_WSASTARTUP_FUNCTION_CALL: { int errorCode; WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); errorCode = WSAStartup(wVersionRequested, &wsaData); if (errorCode != 0) { LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "Could not find a usable Winsock DLL. WSAStartup returned " "an error."; return -1; } if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "Could not find a usable Winsock DLL. WSAStartup did not " "return version 2.2."; WSACleanup(); return -1; } return 0; } default: { LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "Invalid windows initialization called"; return -1; } } return -1; } int TRI_createFile(char const* filename, int openFlags, int modeFlags) { HANDLE fileHandle; int fileDescriptor; fileHandle = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, (openFlags & O_APPEND) ? OPEN_ALWAYS : CREATE_NEW, 0, NULL); if (fileHandle == INVALID_HANDLE_VALUE) { return -1; } if (openFlags & O_APPEND) { SetFilePointer(fileHandle, 0, NULL, FILE_END); } fileDescriptor = _open_osfhandle((intptr_t)fileHandle, O_RDWR | _O_BINARY); return fileDescriptor; } //////////////////////////////////////////////////////////////////////////////// /// @brief opens a file for windows /// /// Creates or opens a file using the windows CreateFile method. Notice below we /// have used the method CreateFileA to avoid unicode characters - for now /// anyway. //////////////////////////////////////////////////////////////////////////////// int TRI_OPEN_WIN32(char const* filename, int openFlags) { static int const O_ACCMODE = 3; HANDLE fileHandle; int fileDescriptor; DWORD mode; switch (openFlags & O_ACCMODE) { case O_RDONLY: mode = GENERIC_READ; break; case O_WRONLY: mode = GENERIC_WRITE; break; case O_RDWR: mode = GENERIC_READ | GENERIC_WRITE; break; } fileHandle = CreateFileA( filename, mode, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (fileHandle == INVALID_HANDLE_VALUE) { return -1; } fileDescriptor = _open_osfhandle((intptr_t)(fileHandle), (openFlags & O_ACCMODE) | _O_BINARY); return fileDescriptor; } //////////////////////////////////////////////////////////////////////////////// /// @brief converts a Windows error to a *nix system error //////////////////////////////////////////////////////////////////////////////// int TRI_MapSystemError(DWORD error) { switch (error) { case ERROR_INVALID_FUNCTION: return EINVAL; case ERROR_FILE_NOT_FOUND: return ENOENT; case ERROR_PATH_NOT_FOUND: return ENOENT; case ERROR_TOO_MANY_OPEN_FILES: return EMFILE; case ERROR_ACCESS_DENIED: return EACCES; case ERROR_INVALID_HANDLE: return EBADF; case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM; case ERROR_INVALID_DATA: return EINVAL; case ERROR_OUTOFMEMORY: return ENOMEM; case ERROR_INVALID_DRIVE: return ENODEV; case ERROR_NOT_SAME_DEVICE: return EXDEV; case ERROR_NO_MORE_FILES: return ENFILE; case ERROR_WRITE_PROTECT: return EROFS; case ERROR_BAD_UNIT: return ENODEV; case ERROR_SHARING_VIOLATION: return EACCES; case ERROR_LOCK_VIOLATION: return EACCES; case ERROR_SHARING_BUFFER_EXCEEDED: return ENOLCK; case ERROR_HANDLE_EOF: return ENODATA; case ERROR_HANDLE_DISK_FULL: return ENOSPC; case ERROR_NOT_SUPPORTED: return ENOSYS; case ERROR_REM_NOT_LIST: return ENFILE; case ERROR_DUP_NAME: return EEXIST; case ERROR_BAD_NETPATH: return EBADF; case ERROR_BAD_NET_NAME: return EBADF; case ERROR_FILE_EXISTS: return EEXIST; case ERROR_CANNOT_MAKE: return EPERM; case ERROR_INVALID_PARAMETER: return EINVAL; case ERROR_NO_PROC_SLOTS: return EAGAIN; case ERROR_BROKEN_PIPE: return EPIPE; case ERROR_OPEN_FAILED: return EIO; case ERROR_NO_MORE_SEARCH_HANDLES: return ENFILE; case ERROR_CALL_NOT_IMPLEMENTED: return ENOSYS; case ERROR_INVALID_NAME: return ENOENT; case ERROR_WAIT_NO_CHILDREN: return ECHILD; case ERROR_CHILD_NOT_COMPLETE: return EBUSY; case ERROR_DIR_NOT_EMPTY: return ENOTEMPTY; case ERROR_SIGNAL_REFUSED: return EIO; case ERROR_BAD_PATHNAME: return ENOENT; case ERROR_SIGNAL_PENDING: return EBUSY; case ERROR_MAX_THRDS_REACHED: return EAGAIN; case ERROR_BUSY: return EBUSY; case ERROR_ALREADY_EXISTS: return EEXIST; case ERROR_NO_SIGNAL_SENT: return EIO; case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG; case ERROR_META_EXPANSION_TOO_LONG: return EINVAL; case ERROR_INVALID_SIGNAL_NUMBER: return EINVAL; case ERROR_THREAD_1_INACTIVE: return EINVAL; case ERROR_BAD_PIPE: return EINVAL; case ERROR_PIPE_BUSY: return EBUSY; case ERROR_NO_DATA: return EPIPE; case ERROR_PIPE_NOT_CONNECTED: return EPIPE; case ERROR_MORE_DATA: return EAGAIN; case ERROR_DIRECTORY: return ENOTDIR; case ERROR_PIPE_CONNECTED: return EBUSY; case ERROR_PIPE_LISTENING: return EPIPE; case ERROR_NO_TOKEN: return EINVAL; case ERROR_PROCESS_ABORTED: return EFAULT; case ERROR_BAD_DEVICE: return ENODEV; case ERROR_BAD_USERNAME: return EINVAL; case ERROR_NOT_CONNECTED: return ENOLINK; case ERROR_OPEN_FILES: return EAGAIN; case ERROR_ACTIVE_CONNECTIONS: return EAGAIN; case ERROR_DEVICE_IN_USE: return EAGAIN; case ERROR_INVALID_AT_INTERRUPT_TIME: return EINTR; case ERROR_IO_DEVICE: return EIO; case ERROR_NOT_OWNER: return EPERM; case ERROR_END_OF_MEDIA: return ENOSPC; case ERROR_EOM_OVERFLOW: return ENOSPC; case ERROR_BEGINNING_OF_MEDIA: return ESPIPE; case ERROR_SETMARK_DETECTED: return ESPIPE; case ERROR_NO_DATA_DETECTED: return ENOSPC; case ERROR_POSSIBLE_DEADLOCK: return EDEADLOCK; case ERROR_CRC: return EIO; case ERROR_NEGATIVE_SEEK: return EINVAL; case ERROR_NOT_READY: return EBADF; case ERROR_DISK_FULL: return ENOSPC; case ERROR_NOACCESS: return EFAULT; case ERROR_FILE_INVALID: return ENXIO; default: return EINVAL; } } static HANDLE hEventLog = INVALID_HANDLE_VALUE; bool TRI_InitWindowsEventLog(void) { hEventLog = RegisterEventSource(NULL, "ArangoDB"); if (NULL == hEventLog) { // well, fail then. return false; } return true; } void TRI_CloseWindowsEventlog(void) { DeregisterEventSource(hEventLog); hEventLog = INVALID_HANDLE_VALUE; } //////////////////////////////////////////////////////////////////////////////// /// @brief logs a message to the windows event log. /// we rather are keen on logging something at all then on being able to work /// with fancy dynamic buffers; thus we work with a static buffer. /// the arango internal logging will handle that usually. //////////////////////////////////////////////////////////////////////////////// // No clue why there is no header for these... #define MSG_INVALID_COMMAND ((DWORD)0xC0020100L) #define UI_CATEGORY ((WORD)0x00000003L) void TRI_LogWindowsEventlog(char const* func, char const* file, int line, std::string const& message) { char buf[1024]; char linebuf[32]; LPCSTR logBuffers[] = {buf, file, func, linebuf, NULL}; TRI_ASSERT(hEventLog != INVALID_HANDLE_VALUE); snprintf(linebuf, sizeof(linebuf), "%d", line); DWORD len = _snprintf(buf, sizeof(buf) - 1, "%s", message.c_str()); buf[sizeof(buf) - 1] = '\0'; // Try to get messages through to windows syslog... if (!ReportEvent(hEventLog, EVENTLOG_ERROR_TYPE, UI_CATEGORY, MSG_INVALID_COMMAND, NULL, 4, 0, (LPCSTR*)logBuffers, NULL)) { // well, fail then... } } void TRI_LogWindowsEventlog(char const* func, char const* file, int line, char const* fmt, va_list ap) { char buf[1024]; char linebuf[32]; LPCSTR logBuffers[] = {buf, file, func, linebuf, NULL}; TRI_ASSERT(hEventLog != INVALID_HANDLE_VALUE); snprintf(linebuf, sizeof(linebuf), "%d", line); DWORD len = _vsnprintf(buf, sizeof(buf) - 1, fmt, ap); buf[sizeof(buf) - 1] = '\0'; // Try to get messages through to windows syslog... if (!ReportEvent(hEventLog, EVENTLOG_ERROR_TYPE, UI_CATEGORY, MSG_INVALID_COMMAND, NULL, 4, 0, (LPCSTR*)logBuffers, NULL)) { // well, fail then... } } void TRI_WindowsEmergencyLog(char const* func, char const* file, int line, char const* fmt, ...) { va_list ap; va_start(ap, fmt); TRI_LogWindowsEventlog(func, file, line, fmt, ap); va_end(ap); } void ADB_WindowsEntryFunction() { int maxOpenFiles = 2048; int res = 0; // ........................................................................... // Uncomment this to call this for extended debug information. // If you familiar with valgrind ... then this is not like that, however // you do get some similar functionality. // ........................................................................... // res = initializeWindows(TRI_WIN_INITIAL_SET_DEBUG_FLAG, 0); res = initializeWindows(TRI_WIN_INITIAL_SET_INVALID_HANLE_HANDLER, 0); if (res != 0) { _exit(EXIT_FAILURE); } res = initializeWindows(TRI_WIN_INITIAL_SET_MAX_STD_IO, (char const*)(&maxOpenFiles)); if (res != 0) { _exit(EXIT_FAILURE); } res = initializeWindows(TRI_WIN_INITIAL_WSASTARTUP_FUNCTION_CALL, 0); if (res != 0) { _exit(EXIT_FAILURE); } TRI_Application_Exit_SetExit(ADB_WindowsExitFunction); } TRI_serviceAbort_t serviceAbort = nullptr; void TRI_SetWindowsServiceAbortFunction(TRI_serviceAbort_t f) { serviceAbort = f; } void ADB_WindowsExitFunction(int exitCode, void* data) { if (serviceAbort != nullptr) { serviceAbort(exitCode); } exit(exitCode); } // Detect cygwin ssh / terminals int _cyg_isatty(int fd) { // detect standard windows ttys: if (_isatty (fd)) { return 1; } // stupid hack to allow forcing a tty..need to understand this better // and create a thorough fix..without this the logging stuff will not // log to the foreground which is super annoying for debugging the // resilience tests char* forcetty = getenv("FORCE_WINDOWS_TTY"); if (forcetty != nullptr) { return strcmp(forcetty, "1") == 0; } HANDLE fh; char buff[sizeof(FILE_NAME_INFO) + sizeof(WCHAR)*MAX_PATH]; FILE_NAME_INFO *FileInformation = (FILE_NAME_INFO*) buff; /* get the HANDLE for the filedescriptor. */ fh = (HANDLE) _get_osfhandle (fd); if (!fh || fh == INVALID_HANDLE_VALUE) { return 0; } /* Cygwin consoles are pipes. If its not, no reason to continue: */ if (GetFileType (fh) != FILE_TYPE_PIPE) { return 0; } if (!GetFileInformationByHandleEx(fh, FileNameInfo, FileInformation, sizeof(buff))) { return 0; } // we expect something along the lines of: \cygwin-0eb90a57d5759b7b-pty3-to-master?? - if we find it its a tty. PWCHAR cp = (PWCHAR) FileInformation->FileName; if (!wcsncmp (cp, L"\\cygwin-", 8) && !wcsncmp (cp + 24, L"-pty", 4)) { cp = wcschr (cp + 28, '-'); if (!cp) { return 0; } if (!wcsncmp (cp, L"-from-master", sizeof("-from-master") - 1) || !wcsncmp (cp, L"-to-master", sizeof("-to-master") -1)) { return 1; } } errno = EINVAL; return 0; } // Detect cygwin ssh / terminals int _is_cyg_tty(int fd) { // detect standard windows ttys: if (_isatty (fd)) { return 0; } HANDLE fh; char buff[sizeof(FILE_NAME_INFO) + sizeof(WCHAR)*MAX_PATH]; FILE_NAME_INFO *FileInformation = (FILE_NAME_INFO*) buff; /* get the HANDLE for the filedescriptor. */ fh = (HANDLE) _get_osfhandle (fd); if (!fh || fh == INVALID_HANDLE_VALUE) { return 0; } /* Cygwin consoles are pipes. If its not, no reason to continue: */ if (GetFileType (fh) != FILE_TYPE_PIPE) { return 0; } if (!GetFileInformationByHandleEx(fh, FileNameInfo, FileInformation, sizeof(buff))) { return 0; } // we expect something along the lines of: \cygwin-0eb90a57d5759b7b-pty3-to-master?? - if we find it its a tty. PWCHAR cp = (PWCHAR) FileInformation->FileName; if (!wcsncmp (cp, L"\\cygwin-", 8) && !wcsncmp (cp + 24, L"-pty", 4)) { cp = wcschr (cp + 28, '-'); if (!cp) { return 0; } if (!wcsncmp (cp, L"-from-master", sizeof("-from-master") - 1) || !wcsncmp (cp, L"-to-master", sizeof("-to-master") -1)) { return 1; } } errno = EINVAL; return 0; } bool terminalKnowsANSIColors() { if (_is_cyg_tty (STDOUT_FILENO)) { // Its a cygwin shell, expected to understand ANSI color codes. return true; } // Windows 8 onwards the CMD window understands ANSI-Colorcodes. return IsWindows8OrGreater(); } std::string getFileNameFromHandle(HANDLE fileHandle) { char buff[sizeof(FILE_NAME_INFO) + sizeof(WCHAR)*MAX_PATH]; FILE_NAME_INFO *FileInformation = (FILE_NAME_INFO*) buff; if (!GetFileInformationByHandleEx(fileHandle, FileNameInfo, FileInformation, sizeof(buff) )) { return std::string(); } return std::string((LPCTSTR)CString(FileInformation->FileName)); }