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 const 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
379std::string
380CreateValidSystemPath(const std::string path)
381{
382 // Windows and its file systems, e.g. NTFS and (ex)FAT(12|16|32),
383 // do not like paths with empty spaces or special symbols.
384 // Some of these symbols are allowed in test names, checked in TestCase::AddTestCase.
385 // We replace them with underlines to ensure they work on Windows.
386 std::regex incompatible_characters(" |:[^\\\\]|<|>|\\*");
387 std::string valid_path;
388 std::regex_replace(std::back_inserter(valid_path),
389 path.begin(),
390 path.end(),
391 incompatible_characters,
392 "_");
393 return valid_path;
394}
395
396} // namespace SystemPath
397
398} // 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.
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.