//////////////////////////////////////////////////////////////////////////////// /// @brief some utilities for windows /// /// @file /// /// DISCLAIMER /// /// Copyright 2014 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 /// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include #include #include "win-utils.h" #include #include #include #include #include "Basics/logging.h" #include "Basics/files.h" #include "Basics/StringUtils.h" #include "Basics/tri-strings.h" using namespace std; using namespace triagens::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; } //////////////////////////////////////////////////////////////////////////////// // Calls the windows Sleep function which always sleeps for milliseconds //////////////////////////////////////////////////////////////////////////////// void TRI_sleep (unsigned long waitTime) { Sleep(waitTime * 1000); } //////////////////////////////////////////////////////////////////////////////// // Calls a timer which waits for a signal after the elapsed time. // The timer is accurate to 100nanoseconds //////////////////////////////////////////////////////////////////////////////// void TRI_usleep (unsigned long waitTime) { int result; HANDLE hTimer = NULL; // stores the handle of the timer object LARGE_INTEGER wTime; // essentially a 64bit number wTime.QuadPart = waitTime * 10; // *10 to change to microseconds wTime.QuadPart = -wTime.QuadPart; // negative indicates relative time elapsed, // Create an unnamed waitable timer. hTimer = CreateWaitableTimer(NULL, 1, NULL); if (hTimer == NULL) { // not much we can do at this low level return; } if (GetLastError() == ERROR_ALREADY_EXISTS) { LOG_FATAL_AND_EXIT("internal error in TRI_usleep()"); } // Set timer to wait for indicated micro seconds. if (! SetWaitableTimer(hTimer, &wTime, 0, NULL, NULL, 0)) { // not much we can do at this low level CloseHandle(hTimer); return; } // Wait for the timer result = WaitForSingleObject(hTimer, INFINITE); if (result != WAIT_OBJECT_0) { CloseHandle(hTimer); LOG_FATAL_AND_EXIT("couldn't wait for timer in TRI_usleep()"); } CloseHandle(hTimer); // todo: go through what the result is e.g. WAIT_OBJECT_0 return; } //////////////////////////////////////////////////////////////////////////////// // 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 LOG_ERROR("Invalid handle parameter passed"); /* if (expression != 0) { wprintf(L"win-utils.c:InvalidParameterHandler:EXPRESSION = %s\n",expression); } else { wprintf(L"win-utils.c:InvalidParameterHandler:EXPRESSION = NULL\n"); } if (function != 0) { wprintf(L"win-utils.c:InvalidParameterHandler:FUNCTION = %s\n",function); } else { wprintf(L"win-utils.c:InvalidParameterHandler:FUNCTION = NULL\n"); } if (file!= 0) { wprintf(L"win-utils.c:InvalidParameterHandler:FILE = %s\n",file); } else { wprintf(L"win-utils.c:InvalidParameterHandler:FILE = NULL\n"); } */ //abort(); // TODO: use the wcstombs_s function to convert wchar to char - since all the above // wchar never will contain 2 byte chars } //////////////////////////////////////////////////////////////////////////////// // Called from the 'main' and performs any initialization requirements which // are specific to windows. // // If this function returns 0, then no errors encountered. If < 0, then the // calling function should terminate the application. If > 0, then the // calling function should decide what to do. //////////////////////////////////////////////////////////////////////////////// int finalizeWindows (const TRI_win_finalize_e finalizeWhat, const char* 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 (finalizeWhat) { case TRI_WIN_FINAL_WSASTARTUP_FUNCTION_CALL: { int result = WSACleanup(); // could this cause error on server termination? if (result != 0) { // can not use LOG_ etc here since the logging may have terminated printf("ERROR: Could not perform a valid Winsock2 cleanup. WSACleanup returned error %d.",result); return -1; } return 0; } default: { // can not use LOG_ etc here since the logging may have terminated printf("ERROR: Invalid windows finalization called"); return -1; } } return -1; } int initializeWindows (const TRI_win_initialize_e initializeWhat, const char* 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_ERROR("Could not find a usable Winsock DLL. WSAStartup returned an error."); return -1; } if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { LOG_ERROR("Could not find a usable Winsock DLL. WSAStartup did not return version 2.2."); WSACleanup(); return -1; } return 0; } default: { LOG_ERROR("Invalid windows initialization called"); return -1; } } return -1; } int TRI_createFile (const char* 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 (const char* filename, int openFlags) { static const int 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 fixes the ICU_DATA environment path //////////////////////////////////////////////////////////////////////////////// void TRI_FixIcuDataEnv () { if (getenv("ICU_DATA") != nullptr) { return; } string p = TRI_LocateInstallDirectory(); if (! p.empty()) { string e = "ICU_DATA=" + p + "\\share\\arangodb\\"; e = StringUtils::replace(e, "\\", "\\\\"); putenv(e.c_str()); } else { #ifdef _SYSCONFDIR_ string e = "ICU_DATA=" + string(_SYSCONFDIR_) + "..\\..\\bin"; e = StringUtils::replace(e, "\\", "\\\\"); putenv(e.c_str()); #else p = TRI_LocateBinaryPath(nullptr); if (! p.empty()) { string e = "ICU_DATA=" + p + "\\"; e = StringUtils::replace(e, "\\", "\\\\"); putenv(e.c_str()); } else { putenv("ICU_DATA=.\\\\"); } #endif } } //////////////////////////////////////////////////////////////////////////////// /// @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; } } //////////////////////////////////////////////////////////////////////////////// /// @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, char const* fmt, va_list ap) { char buf[1024]; char linebuf[32]; LPCSTR logBuffers [] = { buf, file, func, linebuf, NULL }; HANDLE hEventLog = NULL; hEventLog = RegisterEventSource(NULL, "ArangoDB"); if (NULL == hEventLog) { // well, fail then. return; } 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... } DeregisterEventSource(hEventLog); } // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: