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 pathLength;
138 while (true)
139 {
140 pathLength = readlink("/proc/self/exe", buffer, size);
141 if ((pathLength > 0 && pathLength < size) ||
142 (pathLength == -1 && errno != ENAMETOOLONG))
143 {
144 break;
145 }
146 size *= 2;
147 free(buffer);
148 buffer = (char*)malloc(size);
149 memset(buffer, 0, size);
150 }
151 if (pathLength == -1)
152 {
153 NS_FATAL_ERROR("Oops, could not find self directory.");
154 }
155 filename = buffer;
156 free(buffer);
157 }
158#elif defined(__WIN32__)
159 {
160 // LPTSTR = char *
161 DWORD size = 1024;
162 auto lpFilename = (LPTSTR)malloc(sizeof(TCHAR) * size);
163 DWORD pathLength = GetModuleFileName(nullptr, lpFilename, size);
164 while (pathLength == size)
165 {
166 size = size * 2;
167 free(lpFilename);
168 lpFilename = (LPTSTR)malloc(sizeof(TCHAR) * size);
169 pathLength = GetModuleFileName(nullptr, lpFilename, size);
170 }
171 NS_ASSERT(pathLength != 0);
172 filename = lpFilename;
173 free(lpFilename);
174 }
175#elif defined(__APPLE__)
176 {
177 uint32_t bufsize = 1024;
178 char* buffer = (char*)malloc(bufsize);
179 NS_ASSERT(buffer);
180 int status = _NSGetExecutablePath(buffer, &bufsize);
181 if (status == -1)
182 {
183 free(buffer);
184 buffer = (char*)malloc(bufsize);
185 status = _NSGetExecutablePath(buffer, &bufsize);
186 }
187 NS_ASSERT(status == 0);
188 filename = buffer;
189 free(buffer);
190 }
191#elif defined(__FreeBSD__)
192 {
193 int mib[4];
194 std::size_t bufSize = 1024;
195 char* buf = (char*)malloc(bufSize);
196
197 mib[0] = CTL_KERN;
198 mib[1] = KERN_PROC;
199 mib[2] = KERN_PROC_PATHNAME;
200 mib[3] = -1;
201
202 sysctl(mib, 4, buf, &bufSize, nullptr, 0);
203 filename = buf;
204 }
205#endif
206 return filename;
207}
208
209std::string
211{
212 return Dirname(FindSelf());
213}
214
215std::string
216Append(std::string left, std::string right)
217{
218 // removing trailing separators from 'left'
219 NS_LOG_FUNCTION(left << right);
220 while (true)
221 {
222 std::string::size_type lastSep = left.rfind(SYSTEM_PATH_SEP);
223 if (lastSep != left.size() - 1)
224 {
225 break;
226 }
227 left = left.substr(0, left.size() - 1);
228 }
229 std::string retval = left + SYSTEM_PATH_SEP + right;
230 return retval;
231}
232
233std::list<std::string>
234Split(std::string path)
235{
236 NS_LOG_FUNCTION(path);
237 std::vector<std::string> items = SplitString(path, SYSTEM_PATH_SEP);
238 std::list<std::string> retval(items.begin(), items.end());
239 return retval;
240}
241
242std::string
243Join(std::list<std::string>::const_iterator begin, std::list<std::string>::const_iterator end)
244{
245 NS_LOG_FUNCTION(*begin << *end);
246 std::string retval = "";
247 for (auto i = begin; i != end; i++)
248 {
249 if ((*i).empty())
250 {
251 // skip empty strings in the path list
252 continue;
253 }
254 else if (i == begin)
255 {
256 retval = *i;
257 }
258 else
259 {
260 retval = retval + SYSTEM_PATH_SEP + *i;
261 }
262 }
263 return retval;
264}
265
266std::list<std::string>
267ReadFiles(std::string path)
268{
269 NS_LOG_FUNCTION(path);
270 bool err;
271 std::list<std::string> files;
272 std::tie(files, err) = ReadFilesNoThrow(path);
273 if (err)
274 {
275 NS_FATAL_ERROR("Could not open directory=" << path);
276 }
277 return files;
278}
279
280std::string
282{
284 auto [found, path] = EnvironmentVariable::Get("TMP");
285 if (!found)
286 {
287 std::tie(found, path) = EnvironmentVariable::Get("TEMP");
288 if (!found)
289 {
290 path = "/tmp";
291 }
292 }
293
294 //
295 // Just in case the user wants to go back and find the output, we give
296 // a hint as to which dir we created by including a time hint.
297 //
298 time_t now = time(nullptr);
299 struct tm* tm_now = localtime(&now);
300 //
301 // But we also randomize the name in case there are multiple users doing
302 // this at the same time
303 //
304 srand(time(nullptr));
305 long int n = rand();
306
307 //
308 // The final path to the directory is going to look something like
309 //
310 // /tmp/ns3.14.30.29.32767
311 //
312 // The first segment comes from one of the temporary directory env
313 // variables or /tmp if not found. The directory name starts with an
314 // identifier telling folks who is making all of the temp directories
315 // and then the local time (in this case 14.30.29 -- which is 2:30 and
316 // 29 seconds PM).
317 //
318 std::ostringstream oss;
319 oss << path << SYSTEM_PATH_SEP << "ns-3." << tm_now->tm_hour << "." << tm_now->tm_min << "."
320 << tm_now->tm_sec << "." << n;
321
322 return oss.str();
323}
324
325void
326MakeDirectories(std::string path)
327{
328 NS_LOG_FUNCTION(path);
329
330 std::error_code ec;
331 if (!std::filesystem::exists(path))
332 {
333 std::filesystem::create_directories(path, ec);
334 }
335
336 if (ec.value())
337 {
338 NS_FATAL_ERROR("failed creating directory " << path);
339 }
340}
341
342bool
343Exists(const std::string path)
344{
345 NS_LOG_FUNCTION(path);
346
347 bool err;
348 auto dirpath = Dirname(path);
349 std::list<std::string> files;
350 tie(files, err) = ReadFilesNoThrow(dirpath);
351 if (err)
352 {
353 // Directory doesn't exist
354 NS_LOG_LOGIC("directory doesn't exist: " << dirpath);
355 return false;
356 }
357 NS_LOG_LOGIC("directory exists: " << dirpath);
358
359 // Check if the file itself exists
360 auto tokens = Split(path);
361 const std::string& file = tokens.back();
362
363 if (file.empty())
364 {
365 // Last component was a directory, not a file name
366 // We already checked that the directory exists,
367 // so return true
368 NS_LOG_LOGIC("directory path exists: " << path);
369 return true;
370 }
371
372 files = ReadFiles(dirpath);
373
374 auto it = std::find(files.begin(), files.end(), file);
375 if (it == files.end())
376 {
377 // File itself doesn't exist
378 NS_LOG_LOGIC("file itself doesn't exist: " << file);
379 return false;
380 }
381
382 NS_LOG_LOGIC("file itself exists: " << file);
383 return true;
384}
385
386std::string
387CreateValidSystemPath(const std::string path)
388{
389 // Windows and its file systems, e.g. NTFS and (ex)FAT(12|16|32),
390 // do not like paths with empty spaces or special symbols.
391 // Some of these symbols are allowed in test names, checked in TestCase::AddTestCase.
392 // We replace them with underlines to ensure they work on Windows.
393 std::regex incompatible_characters(" |:[^\\\\]|<|>|\\*");
394 std::string valid_path;
395 std::regex_replace(std::back_inserter(valid_path),
396 path.begin(),
397 path.end(),
398 incompatible_characters,
399 "_");
400 return valid_path;
401}
402
403} // namespace SystemPath
404
405} // 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:194
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition log.h:274
#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.
std::string FindSelf()
Get the file system path to the current executable.
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.
Namespace for various file and directory path functions.
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.