A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
system-path.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2008 INRIA
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
7 */
8#include "system-path.h"
9
10#include "assert.h"
12#include "fatal-error.h"
13#include "log.h"
14#include "string.h"
15
16#include <algorithm>
17#include <ctime>
18#include <filesystem>
19#include <regex>
20#include <sstream>
21#include <tuple>
22
23#ifdef __APPLE__
24#include <mach-o/dyld.h>
25#endif /* __APPLE__ */
26
27#ifdef __FreeBSD__
28#include <sys/sysctl.h>
29#include <sys/types.h>
30#endif
31
32#ifdef __linux__
33#include <cstring>
34#include <unistd.h>
35#endif
36
37#ifdef __WIN32__
38#define WIN32_LEAN_AND_MEAN
39#include <regex>
40#include <windows.h>
41#endif
42
43/**
44 * System-specific path separator used between directory names.
45 */
46#if defined(__WIN32__)
47constexpr auto SYSTEM_PATH_SEP = "\\";
48#else
49constexpr auto SYSTEM_PATH_SEP = "/";
50#endif
51
52/**
53 * \file
54 * \ingroup systempath
55 * ns3::SystemPath implementation.
56 */
57
58namespace ns3
59{
60
61NS_LOG_COMPONENT_DEFINE("SystemPath");
62
63// unnamed namespace for internal linkage
64namespace
65{
66/**
67 * \ingroup systempath
68 * Get the list of files located in a file system directory with error.
69 *
70 * \param [in] path A path which identifies a directory
71 * \return Tuple with a list of the filenames which are located in the input directory or error flag
72 * \c true if directory doesn't exist.
73 */
74std::tuple<std::list<std::string>, bool>
75ReadFilesNoThrow(std::string path)
76{
77 NS_LOG_FUNCTION(path);
78 std::list<std::string> files;
79 if (!std::filesystem::exists(path))
80 {
81 return std::make_tuple(files, true);
82 }
83 for (auto& it : std::filesystem::directory_iterator(path))
84 {
85 if (!std::filesystem::is_directory(it.path()))
86 {
87 files.push_back(it.path().filename().string());
88 }
89 }
90 return std::make_tuple(files, false);
91}
92
93} // unnamed namespace
94
95namespace SystemPath
96{
97
98/**
99 * \ingroup systempath
100 * \brief Get the directory path for a file.
101 *
102 * This is an internal function (by virtue of not being
103 * declared in a \c .h file); the public API is FindSelfDirectory().
104 *
105 * \param [in] path The full path to a file.
106 * \returns The full path to the containing directory.
107 */
108std::string
109Dirname(std::string path)
110{
111 NS_LOG_FUNCTION(path);
112 std::list<std::string> elements = Split(path);
113 auto last = elements.end();
114 last--;
115 return Join(elements.begin(), last);
116}
117
118std::string
120{
121 /**
122 * This function returns the path to the running $PREFIX.
123 * Mac OS X: _NSGetExecutablePath() (man 3 dyld)
124 * Linux: readlink /proc/self/exe
125 * Solaris: getexecname()
126 * FreeBSD: sysctl CTL_KERN KERN_PROC KERN_PROC_PATHNAME -1
127 * BSD with procfs: readlink /proc/curproc/file
128 * Windows: GetModuleFileName() with hModule = NULL
129 */
131 std::string filename;
132#if defined(__linux__)
133 {
134 ssize_t size = 1024;
135 char* buffer = (char*)malloc(size);
136 memset(buffer, 0, size);
137 int status;
138 while (true)
139 {
140 status = readlink("/proc/self/exe", buffer, size);
141 if (status != 1 || (status == -1 && errno != ENAMETOOLONG))
142 {
143 break;
144 }
145 size *= 2;
146 free(buffer);
147 buffer = (char*)malloc(size);
148 memset(buffer, 0, size);
149 }
150 if (status == -1)
151 {
152 NS_FATAL_ERROR("Oops, could not find self directory.");
153 }
154 filename = buffer;
155 free(buffer);
156 }
157#elif defined(__WIN32__)
158 {
159 // LPTSTR = char *
160 DWORD size = 1024;
161 auto lpFilename = (LPTSTR)malloc(sizeof(TCHAR) * size);
162 DWORD status = GetModuleFileName(nullptr, lpFilename, size);
163 while (status == size)
164 {
165 size = size * 2;
166 free(lpFilename);
167 lpFilename = (LPTSTR)malloc(sizeof(TCHAR) * size);
168 status = GetModuleFileName(nullptr, lpFilename, size);
169 }
170 NS_ASSERT(status != 0);
171 filename = lpFilename;
172 free(lpFilename);
173 }
174#elif defined(__APPLE__)
175 {
176 uint32_t bufsize = 1024;
177 char* buffer = (char*)malloc(bufsize);
178 NS_ASSERT(buffer);
179 int status = _NSGetExecutablePath(buffer, &bufsize);
180 if (status == -1)
181 {
182 free(buffer);
183 buffer = (char*)malloc(bufsize);
184 status = _NSGetExecutablePath(buffer, &bufsize);
185 }
186 NS_ASSERT(status == 0);
187 filename = buffer;
188 free(buffer);
189 }
190#elif defined(__FreeBSD__)
191 {
192 int mib[4];
193 std::size_t bufSize = 1024;
194 char* buf = (char*)malloc(bufSize);
195
196 mib[0] = CTL_KERN;
197 mib[1] = KERN_PROC;
198 mib[2] = KERN_PROC_PATHNAME;
199 mib[3] = -1;
200
201 sysctl(mib, 4, buf, &bufSize, nullptr, 0);
202 filename = buf;
203 }
204#endif
205 return Dirname(filename);
206}
207
208std::string
209Append(std::string left, std::string right)
210{
211 // removing trailing separators from 'left'
212 NS_LOG_FUNCTION(left << right);
213 while (true)
214 {
215 std::string::size_type lastSep = left.rfind(SYSTEM_PATH_SEP);
216 if (lastSep != left.size() - 1)
217 {
218 break;
219 }
220 left = left.substr(0, left.size() - 1);
221 }
222 std::string retval = left + SYSTEM_PATH_SEP + right;
223 return retval;
224}
225
226std::list<std::string>
227Split(std::string path)
228{
229 NS_LOG_FUNCTION(path);
230 std::vector<std::string> items = SplitString(path, SYSTEM_PATH_SEP);
231 std::list<std::string> retval(items.begin(), items.end());
232 return retval;
233}
234
235std::string
236Join(std::list<std::string>::const_iterator begin, std::list<std::string>::const_iterator end)
237{
238 NS_LOG_FUNCTION(*begin << *end);
239 std::string retval = "";
240 for (auto i = begin; i != end; i++)
241 {
242 if ((*i).empty())
243 {
244 // skip empty strings in the path list
245 continue;
246 }
247 else if (i == begin)
248 {
249 retval = *i;
250 }
251 else
252 {
253 retval = retval + SYSTEM_PATH_SEP + *i;
254 }
255 }
256 return retval;
257}
258
259std::list<std::string>
260ReadFiles(std::string path)
261{
262 NS_LOG_FUNCTION(path);
263 bool err;
264 std::list<std::string> files;
265 std::tie(files, err) = ReadFilesNoThrow(path);
266 if (err)
267 {
268 NS_FATAL_ERROR("Could not open directory=" << path);
269 }
270 return files;
271}
272
273std::string
275{
277 auto [found, path] = EnvironmentVariable::Get("TMP");
278 if (!found)
279 {
280 std::tie(found, path) = EnvironmentVariable::Get("TEMP");
281 if (!found)
282 {
283 path = "/tmp";
284 }
285 }
286
287 //
288 // Just in case the user wants to go back and find the output, we give
289 // a hint as to which dir we created by including a time hint.
290 //
291 time_t now = time(nullptr);
292 struct tm* tm_now = localtime(&now);
293 //
294 // But we also randomize the name in case there are multiple users doing
295 // this at the same time
296 //
297 srand(time(nullptr));
298 long int n = rand();
299
300 //
301 // The final path to the directory is going to look something like
302 //
303 // /tmp/ns3.14.30.29.32767
304 //
305 // The first segment comes from one of the temporary directory env
306 // variables or /tmp if not found. The directory name starts with an
307 // identifier telling folks who is making all of the temp directories
308 // and then the local time (in this case 14.30.29 -- which is 2:30 and
309 // 29 seconds PM).
310 //
311 std::ostringstream oss;
312 oss << path << SYSTEM_PATH_SEP << "ns-3." << tm_now->tm_hour << "." << tm_now->tm_min << "."
313 << tm_now->tm_sec << "." << n;
314
315 return oss.str();
316}
317
318void
319MakeDirectories(std::string path)
320{
321 NS_LOG_FUNCTION(path);
322
323 std::error_code ec;
324 if (!std::filesystem::exists(path))
325 {
326 std::filesystem::create_directories(path, ec);
327 }
328
329 if (ec.value())
330 {
331 NS_FATAL_ERROR("failed creating directory " << path);
332 }
333}
334
335bool
336Exists(const std::string path)
337{
338 NS_LOG_FUNCTION(path);
339
340 bool err;
341 auto dirpath = Dirname(path);
342 std::list<std::string> files;
343 tie(files, err) = ReadFilesNoThrow(dirpath);
344 if (err)
345 {
346 // Directory doesn't exist
347 NS_LOG_LOGIC("directory doesn't exist: " << dirpath);
348 return false;
349 }
350 NS_LOG_LOGIC("directory exists: " << dirpath);
351
352 // Check if the file itself exists
353 auto tokens = Split(path);
354 std::string file = tokens.back();
355
356 if (file.empty())
357 {
358 // Last component was a directory, not a file name
359 // We already checked that the directory exists,
360 // so return true
361 NS_LOG_LOGIC("directory path exists: " << path);
362 return true;
363 }
364
365 files = ReadFiles(dirpath);
366
367 auto it = std::find(files.begin(), files.end(), file);
368 if (it == files.end())
369 {
370 // File itself doesn't exist
371 NS_LOG_LOGIC("file itself doesn't exist: " << file);
372 return false;
373 }
374
375 NS_LOG_LOGIC("file itself exists: " << file);
376 return true;
377
378} // Exists()
379
380std::string
381CreateValidSystemPath(const std::string path)
382{
383 // Windows and its file systems, e.g. NTFS and (ex)FAT(12|16|32),
384 // do not like paths with empty spaces or special symbols.
385 // Some of these symbols are allowed in test names, checked in TestCase::AddTestCase.
386 // We replace them with underlines to ensure they work on Windows.
387 std::regex incompatible_characters(" |:[^\\\\]|<|>|\\*");
388 std::string valid_path;
389 std::regex_replace(std::back_inserter(valid_path),
390 path.begin(),
391 path.end(),
392 incompatible_characters,
393 "_");
394 return valid_path;
395} // CreateValidSystemPath
396
397} // namespace SystemPath
398
399} // namespace ns3
NS_ASSERT() and NS_ASSERT_MSG() macro definitions.
static KeyFoundType Get(const std::string &envvar, const std::string &key="", const std::string &delim=";")
Get the value corresponding to a key from an environment variable.
Class Environment declaration.
NS_FATAL_x macro definitions.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition log.h:271
#define NS_LOG_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
std::list< std::string > ReadFiles(std::string path)
Get the list of files located in a file system directory.
bool Exists(const std::string path)
Check if a path exists.
std::list< std::string > Split(std::string path)
Split a file system path into directories according to the local path separator.
std::string Dirname(std::string path)
Get the directory path for a file.
void MakeDirectories(std::string path)
Create all the directories leading to path.
std::string MakeTemporaryDirectoryName()
Get the name of a temporary directory.
std::string Append(std::string left, std::string right)
Join two file system path elements.
std::string Join(std::list< std::string >::const_iterator begin, std::list< std::string >::const_iterator end)
Join a list of file system path directories into a single file system path.
std::string CreateValidSystemPath(const std::string path)
Replace incompatible characters in a path, to get a path compatible with different file systems.
std::tuple< std::list< std::string >, bool > ReadFilesNoThrow(std::string path)
Get the list of files located in a file system directory with error.
std::string FindSelfDirectory()
Get the file system path to the current executable.
Debug message logging.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
StringVector SplitString(const std::string &str, const std::string &delim)
Split a string on a delimiter.
Definition string.cc:23
ns3::StringValue attribute value declarations.
constexpr auto SYSTEM_PATH_SEP
System-specific path separator used between directory names.
ns3::SystemPath declarations.