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