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 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
18 */
19#include "system-path.h"
20
21#include "assert.h"
23#include "fatal-error.h"
24#include "log.h"
25#include "string.h"
26
27#include <algorithm>
28#include <ctime>
29#include <regex>
30#include <sstream>
31#include <tuple>
32
33// Some compilers such as GCC < 8 (Ubuntu 18.04
34// ships with GCC 7) do not ship with the
35// std::filesystem header, but with the
36// std::experimental::filesystem header.
37// Since Clang reuses these headers and the libstdc++
38// from GCC, we need to either use the experimental
39// version or require a more up-to-date GCC.
40// we use the "fs" namespace to prevent collisions
41// with musl libc.
42#ifdef __has_include
43#if __has_include(<filesystem>)
44#include <filesystem>
45namespace fs = std::filesystem;
46#elif __has_include(<experimental/filesystem>)
47#include <experimental/filesystem>
48namespace fs = std::experimental::filesystem;
49#else
50#error "No support for filesystem library"
51#endif
52#endif
53
54#ifdef __APPLE__
55#include <mach-o/dyld.h>
56#endif /* __APPLE__ */
57
58#ifdef __FreeBSD__
59#include <sys/sysctl.h>
60#include <sys/types.h>
61#endif
62
63#ifdef __linux__
64#include <cstring>
65#include <unistd.h>
66#endif
67
68#ifdef __WIN32__
69#define WIN32_LEAN_AND_MEAN
70#include <regex>
71#include <windows.h>
72#endif
73
77#if defined(__WIN32__)
78constexpr auto SYSTEM_PATH_SEP = "\\";
79#else
80constexpr auto SYSTEM_PATH_SEP = "/";
81#endif
82
89namespace ns3
90{
91
92NS_LOG_COMPONENT_DEFINE("SystemPath");
93
94// unnamed namespace for internal linkage
95namespace
96{
105std::tuple<std::list<std::string>, bool>
106ReadFilesNoThrow(std::string path)
107{
108 NS_LOG_FUNCTION(path);
109 std::list<std::string> files;
110 if (!fs::exists(path))
111 {
112 return std::make_tuple(files, true);
113 }
114 for (auto& it : fs::directory_iterator(path))
115 {
116 if (!fs::is_directory(it.path()))
117 {
118 files.push_back(it.path().filename().string());
119 }
120 }
121 return std::make_tuple(files, false);
122}
123
124} // unnamed namespace
125
126namespace SystemPath
127{
128
139std::string
140Dirname(std::string path)
141{
142 NS_LOG_FUNCTION(path);
143 std::list<std::string> elements = Split(path);
144 std::list<std::string>::const_iterator last = elements.end();
145 last--;
146 return Join(elements.begin(), last);
147}
148
149std::string
151{
162 std::string filename;
163#if defined(__linux__)
164 {
165 ssize_t size = 1024;
166 char* buffer = (char*)malloc(size);
167 memset(buffer, 0, size);
168 int status;
169 while (true)
170 {
171 status = readlink("/proc/self/exe", buffer, size);
172 if (status != 1 || (status == -1 && errno != ENAMETOOLONG))
173 {
174 break;
175 }
176 size *= 2;
177 free(buffer);
178 buffer = (char*)malloc(size);
179 memset(buffer, 0, size);
180 }
181 if (status == -1)
182 {
183 NS_FATAL_ERROR("Oops, could not find self directory.");
184 }
185 filename = buffer;
186 free(buffer);
187 }
188#elif defined(__WIN32__)
189 {
190 // LPTSTR = char *
191 DWORD size = 1024;
192 LPTSTR lpFilename = (LPTSTR)malloc(sizeof(TCHAR) * size);
193 DWORD status = GetModuleFileName(nullptr, lpFilename, size);
194 while (status == size)
195 {
196 size = size * 2;
197 free(lpFilename);
198 lpFilename = (LPTSTR)malloc(sizeof(TCHAR) * size);
199 status = GetModuleFileName(nullptr, lpFilename, size);
200 }
201 NS_ASSERT(status != 0);
202 filename = lpFilename;
203 free(lpFilename);
204 }
205#elif defined(__APPLE__)
206 {
207 uint32_t bufsize = 1024;
208 char* buffer = (char*)malloc(bufsize);
209 NS_ASSERT(buffer);
210 int status = _NSGetExecutablePath(buffer, &bufsize);
211 if (status == -1)
212 {
213 free(buffer);
214 buffer = (char*)malloc(bufsize);
215 status = _NSGetExecutablePath(buffer, &bufsize);
216 }
217 NS_ASSERT(status == 0);
218 filename = buffer;
219 free(buffer);
220 }
221#elif defined(__FreeBSD__)
222 {
223 int mib[4];
224 std::size_t bufSize = 1024;
225 char* buf = (char*)malloc(bufSize);
226
227 mib[0] = CTL_KERN;
228 mib[1] = KERN_PROC;
229 mib[2] = KERN_PROC_PATHNAME;
230 mib[3] = -1;
231
232 sysctl(mib, 4, buf, &bufSize, nullptr, 0);
233 filename = buf;
234 }
235#endif
236 return Dirname(filename);
237}
238
239std::string
240Append(std::string left, std::string right)
241{
242 // removing trailing separators from 'left'
243 NS_LOG_FUNCTION(left << right);
244 while (true)
245 {
246 std::string::size_type lastSep = left.rfind(SYSTEM_PATH_SEP);
247 if (lastSep != left.size() - 1)
248 {
249 break;
250 }
251 left = left.substr(0, left.size() - 1);
252 }
253 std::string retval = left + SYSTEM_PATH_SEP + right;
254 return retval;
255}
256
257std::list<std::string>
258Split(std::string path)
259{
260 NS_LOG_FUNCTION(path);
261 std::vector<std::string> items = SplitString(path, SYSTEM_PATH_SEP);
262 std::list<std::string> retval(items.begin(), items.end());
263 return retval;
264}
265
266std::string
267Join(std::list<std::string>::const_iterator begin, std::list<std::string>::const_iterator end)
268{
269 NS_LOG_FUNCTION(*begin << *end);
270 std::string retval = "";
271 for (std::list<std::string>::const_iterator i = begin; i != end; i++)
272 {
273 if ((*i).empty())
274 {
275 // skip empty strings in the path list
276 continue;
277 }
278 else if (i == begin)
279 {
280 retval = *i;
281 }
282 else
283 {
284 retval = retval + SYSTEM_PATH_SEP + *i;
285 }
286 }
287 return retval;
288}
289
290std::list<std::string>
291ReadFiles(std::string path)
292{
293 NS_LOG_FUNCTION(path);
294 bool err;
295 std::list<std::string> files;
296 std::tie(files, err) = ReadFilesNoThrow(path);
297 if (err)
298 {
299 NS_FATAL_ERROR("Could not open directory=" << path);
300 }
301 return files;
302}
303
304std::string
306{
308 auto [found, path] = EnvironmentVariable::Get("TMP");
309 if (!found)
310 {
311 std::tie(found, path) = EnvironmentVariable::Get("TEMP");
312 if (!found)
313 {
314 path = "/tmp";
315 }
316 }
317
318 //
319 // Just in case the user wants to go back and find the output, we give
320 // a hint as to which dir we created by including a time hint.
321 //
322 time_t now = time(nullptr);
323 struct tm* tm_now = localtime(&now);
324 //
325 // But we also randomize the name in case there are multiple users doing
326 // this at the same time
327 //
328 srand(time(nullptr));
329 long int n = rand();
330
331 //
332 // The final path to the directory is going to look something like
333 //
334 // /tmp/ns3.14.30.29.32767
335 //
336 // The first segment comes from one of the temporary directory env
337 // variables or /tmp if not found. The directory name starts with an
338 // identifier telling folks who is making all of the temp directories
339 // and then the local time (in this case 14.30.29 -- which is 2:30 and
340 // 29 seconds PM).
341 //
342 std::ostringstream oss;
343 oss << path << SYSTEM_PATH_SEP << "ns-3." << tm_now->tm_hour << "." << tm_now->tm_min << "."
344 << tm_now->tm_sec << "." << n;
345
346 return oss.str();
347}
348
349void
350MakeDirectories(std::string path)
351{
352 NS_LOG_FUNCTION(path);
353
354 std::error_code ec;
355 if (!fs::exists(path))
356 {
357 fs::create_directories(path, ec);
358 }
359
360 if (ec.value())
361 {
362 NS_FATAL_ERROR("failed creating directory " << path);
363 }
364}
365
366bool
367Exists(const std::string path)
368{
369 NS_LOG_FUNCTION(path);
370
371 bool err;
372 auto dirpath = Dirname(path);
373 std::list<std::string> files;
374 tie(files, err) = ReadFilesNoThrow(dirpath);
375 if (err)
376 {
377 // Directory doesn't exist
378 NS_LOG_LOGIC("directory doesn't exist: " << dirpath);
379 return false;
380 }
381 NS_LOG_LOGIC("directory exists: " << dirpath);
382
383 // Check if the file itself exists
384 auto tokens = Split(path);
385 std::string file = tokens.back();
386
387 if (file.empty())
388 {
389 // Last component was a directory, not a file name
390 // We already checked that the directory exists,
391 // so return true
392 NS_LOG_LOGIC("directory path exists: " << path);
393 return true;
394 }
395
396 files = ReadFiles(dirpath);
397
398 auto it = std::find(files.begin(), files.end(), file);
399 if (it == files.end())
400 {
401 // File itself doesn't exist
402 NS_LOG_LOGIC("file itself doesn't exist: " << file);
403 return false;
404 }
405
406 NS_LOG_LOGIC("file itself exists: " << file);
407 return true;
408
409} // Exists()
410
411std::string
412CreateValidSystemPath(const std::string path)
413{
414 // Windows and its file systems, e.g. NTFS and (ex)FAT(12|16|32),
415 // do not like paths with empty spaces or special symbols.
416 // Some of these symbols are allowed in test names, checked in TestCase::AddTestCase.
417 // We replace them with underlines to ensure they work on Windows.
418 std::regex incompatible_characters(" |:[^\\\\]|<|>|\\*");
419 std::string valid_path;
420 std::regex_replace(std::back_inserter(valid_path),
421 path.begin(),
422 path.end(),
423 incompatible_characters,
424 "_");
425 return valid_path;
426} // CreateValidSystemPath
427
428} // namespace SystemPath
429
430} // 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:66
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:179
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:282
#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.
Definition: system-path.cc:291
bool Exists(const std::string path)
Check if a path exists.
Definition: system-path.cc:367
std::list< std::string > Split(std::string path)
Split a file system path into directories according to the local path separator.
Definition: system-path.cc:258
std::string Dirname(std::string path)
Get the directory path for a file.
Definition: system-path.cc:140
void MakeDirectories(std::string path)
Create all the directories leading to path.
Definition: system-path.cc:350
std::string MakeTemporaryDirectoryName()
Get the name of a temporary directory.
Definition: system-path.cc:305
std::string Append(std::string left, std::string right)
Join two file system path elements.
Definition: system-path.cc:240
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.
Definition: system-path.cc:267
std::string CreateValidSystemPath(const std::string path)
Replace incompatible characters in a path, to get a path compatible with different file systems.
Definition: system-path.cc:412
std::tuple< std::list< std::string >, bool > ReadFilesNoThrow(std::string path)
Get the list of files located in a file system directory with error.
Definition: system-path.cc:106
std::string FindSelfDirectory()
Get the file system path to the current executable.
Definition: system-path.cc:150
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:34
ns3::StringValue attribute value declarations.
constexpr auto SYSTEM_PATH_SEP
System-specific path separator used between directory names.
Definition: system-path.cc:80
ns3::SystemPath declarations.