1 /* 2 Operating system dependent functionality. 3 4 Copyright (C) 2013 AMPL Optimization Inc 5 6 Permission to use, copy, modify, and distribute this software and its 7 documentation for any purpose and without fee is hereby granted, 8 provided that the above copyright notice appear in all copies and that 9 both that the copyright notice and this permission notice and warranty 10 disclaimer appear in supporting documentation. 11 12 The author and AMPL Optimization Inc disclaim all warranties with 13 regard to this software, including all implied warranties of 14 merchantability and fitness. In no event shall the author be liable 15 for any special, indirect or consequential damages or any damages 16 whatsoever resulting from loss of use, data or profits, whether in an 17 action of contract, negligence or other tortious action, arising out 18 of or in connection with the use or performance of this software. 19 20 Author: Victor Zverovich 21 */ 22 23 #include "mp/os.h" 24 #include "mp/error.h" 25 #include "mp/posix.h" 26 27 #include <cerrno> 28 #include <cstdlib> 29 #include <cstring> // for std::strlen 30 #include <algorithm> 31 32 #ifndef _WIN32 33 # include <sys/mman.h> 34 # include <sys/types.h> 35 # include <sys/stat.h> 36 # include <fcntl.h> 37 # include <sys/param.h> // __FreeBSD__ 38 #endif 39 40 #if defined(__APPLE__) 41 # include <mach-o/dyld.h> 42 #elif defined(_WIN32) 43 struct IUnknown; // Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here" when using /permissive- 44 # include <windows.h> 45 # include <io.h> 46 # undef min 47 #else 48 # include <unistd.h> 49 #endif 50 51 #undef getenv 52 53 using std::size_t; 54 using fmt::SystemError; 55 using mp::path; 56 57 // Workaround for a bug in MSVC. 58 // http://connect.microsoft.com/VisualStudio/feedback/details/ 59 // 786583/in-class-static-const-member-initialization-and-lnk2005 60 #ifndef _MSC_EXTENSIONS 61 const char path::preferred_separator; 62 #endif 63 64 #ifndef _WIN32 65 66 # if defined(__linux__) 67 68 // Linux implementation. 69 path mp::GetExecutablePath() { 70 fmt::internal::MemoryBuffer<char, BUFFER_SIZE> buffer; 71 buffer.resize(BUFFER_SIZE); 72 ssize_t size = 0; 73 for (;;) { 74 size = readlink("/proc/self/exe", &buffer[0], buffer.size()); 75 if (size < 0) 76 throw SystemError(errno, "cannot get executable path"); 77 if (static_cast<size_t>(size) != buffer.size()) break; 78 buffer.resize(2 * buffer.size()); 79 } 80 const char *s = &buffer[0]; 81 return path(s, s + size); 82 } 83 84 # elif defined(__APPLE__) 85 86 // Mac OS X implementation. 87 path mp::GetExecutablePath() { 88 fmt::internal::MemoryBuffer<char, BUFFER_SIZE> buffer; 89 uint32_t size = BUFFER_SIZE; 90 buffer.resize(size); 91 if (_NSGetExecutablePath(&buffer[0], &size) != 0) { 92 buffer.resize(size); 93 if (_NSGetExecutablePath(&buffer[0], &size) != 0) 94 throw SystemError(errno, "cannot get executable path"); 95 } 96 if (size == BUFFER_SIZE) 97 size = std::strlen(&buffer[0]); 98 const char *s = &buffer[0]; 99 return path(s, s + size); 100 } 101 102 # elif defined(__sun) 103 104 // Solaris implementation. 105 path mp::GetExecutablePath() { 106 return path(getexecname()); 107 } 108 109 # elif defined(__FreeBSD__) 110 111 path mp::GetExecutablePath() { 112 using namespace std; 113 return path(getprogname()); 114 } 115 # else 116 path mp::GetExecutablePath() { 117 throw "GetExecutablePath() is not implemented for this system"; 118 return path(""); 119 } 120 # endif 121 122 // POSIX implementation. 123 124 path path::temp_directory_path() { 125 const char *dir = std::getenv("TMPDIR"); 126 if (!dir) { 127 # ifdef P_tmpdir 128 dir = P_tmpdir; 129 # else 130 dir = "/tmp"; 131 # endif 132 } 133 return path(dir); 134 } 135 136 void mp::internal::MemoryMappedFileBase::map(int fd, std::size_t size) { 137 char *start = reinterpret_cast<char*>( 138 mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0)); 139 if (start == MAP_FAILED) 140 throw SystemError(errno, "cannot map file"); 141 start_ = start; 142 size_ = size; 143 } 144 145 void mp::internal::MemoryMappedFileBase::unmap() { 146 char *start = start_; 147 start_ = 0; 148 if (munmap(start, size_) == -1) 149 fmt::report_system_error(errno, "cannot unmap file"); 150 } 151 152 #else // _WIN32 153 154 // Windows implementation. 155 156 using fmt::WindowsError; 157 158 path path::temp_directory_path() { 159 enum { BUFFER_SIZE = MAX_PATH + 1 }; 160 wchar_t buffer[BUFFER_SIZE]; 161 DWORD result = GetTempPathW(BUFFER_SIZE, &buffer[0]); 162 if (result == 0) { 163 throw WindowsError( 164 GetLastError(), "cannot get path to the temporary directory"); 165 } 166 assert(result <= BUFFER_SIZE); 167 buffer[BUFFER_SIZE - 1] = L'\0'; 168 fmt::internal::UTF16ToUTF8 utf8_str(buffer); 169 const char *s = utf8_str.c_str(); 170 return path(s, s + utf8_str.size()); 171 } 172 173 path mp::GetExecutablePath() { 174 fmt::internal::MemoryBuffer<wchar_t, BUFFER_SIZE> buffer; 175 buffer.resize(BUFFER_SIZE); 176 DWORD size = 0; 177 for (;;) { 178 size = GetModuleFileNameW(0, &buffer[0], static_cast<DWORD>(buffer.size())); 179 if (size == 0) 180 throw WindowsError(GetLastError(), "cannot get executable path"); 181 if (size < buffer.size()) break; 182 buffer.resize(2 * buffer.size()); 183 } 184 fmt::internal::UTF16ToUTF8 utf8_str(&buffer[0]); 185 const char *s = utf8_str.c_str(); 186 return path(s, s + utf8_str.size()); 187 } 188 189 void mp::internal::MemoryMappedFileBase::map(int fd, std::size_t size) { 190 class Handle { 191 HANDLE handle_; 192 Handle(const Handle &) {} 193 void operator=(const Handle &) {} 194 public: 195 explicit Handle(HANDLE h) : handle_(h) {} 196 ~Handle() { CloseHandle(handle_); } 197 operator HANDLE() const { return handle_; } 198 }; 199 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd)); 200 if (handle == INVALID_HANDLE_VALUE) 201 throw SystemError(errno, "cannot get file handle"); 202 Handle mapping(CreateFileMappingW(handle, 0, PAGE_READONLY, 0, 0, 0)); 203 if (!mapping) 204 throw WindowsError(GetLastError(), "cannot create file mapping"); 205 char *start = reinterpret_cast<char*>( 206 MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0)); 207 if (!start) 208 throw WindowsError(GetLastError(), "cannot map file"); 209 start_ = start; 210 size_ = size; 211 } 212 213 void mp::internal::MemoryMappedFileBase::unmap() { 214 char *start = start_; 215 start_ = 0; 216 if (!UnmapViewOfFile(start)) 217 throw WindowsError(GetLastError(), "cannot unmap file"); 218 } 219 220 #endif // _WIN32 221