A Discrete-Event Network Simulator
API
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"
22#include "fatal-error.h"
23#include "log.h"
24
25#include <algorithm>
26#include <cstdlib> // getenv
27#include <cstring> // strlen
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 <unistd.h>
65#endif
66
67#ifdef __WIN32__
68#define WIN32_LEAN_AND_MEAN
69#include <regex>
70#include <windows.h>
71#endif
72
76#if defined(__WIN32__)
77constexpr auto SYSTEM_PATH_SEP = "\\";
78#else
79constexpr auto SYSTEM_PATH_SEP = "/";
80#endif
81
88namespace ns3
89{
90
91NS_LOG_COMPONENT_DEFINE("SystemPath");
92
93// unnamed namespace for internal linkage
94namespace
95{
104std::tuple<std::list<std::string>, bool>
106{
108 std::list<std::string> files;
109 if (!fs::exists(path))
110 {
111 return std::make_tuple(files, true);
112 }
113 for (auto& it : fs::directory_iterator(path))
114 {
115 if (!fs::is_directory(it.path()))
116 {
117 files.push_back(it.path().filename().string());
118 }
119 }
120 return std::make_tuple(files, false);
121}
122
123} // unnamed namespace
124
125namespace SystemPath
126{
127
138std::string
139Dirname(std::string path)
140{
142 std::list<std::string> elements = Split(path);
143 std::list<std::string>::const_iterator last = elements.end();
144 last--;
145 return Join(elements.begin(), last);
146}
147
148std::string
150{
161 std::string filename;
162#if defined(__linux__)
163 {
164 ssize_t size = 1024;
165 char* buffer = (char*)malloc(size);
166 memset(buffer, 0, size);
167 int status;
168 while (true)
169 {
170 status = readlink("/proc/self/exe", buffer, size);
171 if (status != 1 || (status == -1 && errno != ENAMETOOLONG))
172 {
173 break;
174 }
175 size *= 2;
176 free(buffer);
177 buffer = (char*)malloc(size);
178 memset(buffer, 0, size);
179 }
180 if (status == -1)
181 {
182 NS_FATAL_ERROR("Oops, could not find self directory.");
183 }
184 filename = buffer;
185 free(buffer);
186 }
187#elif defined(__WIN32__)
188 {
189 // LPTSTR = char *
190 DWORD size = 1024;
191 LPTSTR lpFilename = (LPTSTR)malloc(sizeof(TCHAR) * size);
192 DWORD status = GetModuleFileName(nullptr, lpFilename, size);
193 while (status == size)
194 {
195 size = size * 2;
196 free(lpFilename);
197 lpFilename = (LPTSTR)malloc(sizeof(TCHAR) * size);
198 status = GetModuleFileName(nullptr, lpFilename, size);
199 }
200 NS_ASSERT(status != 0);
201 filename = lpFilename;
202 free(lpFilename);
203 }
204#elif defined(__APPLE__)
205 {
206 uint32_t bufsize = 1024;
207 char* buffer = (char*)malloc(bufsize);
208 NS_ASSERT(buffer);
209 int status = _NSGetExecutablePath(buffer, &bufsize);
210 if (status == -1)
211 {
212 free(buffer);
213 buffer = (char*)malloc(bufsize);
214 status = _NSGetExecutablePath(buffer, &bufsize);
215 }
216 NS_ASSERT(status == 0);
217 filename = buffer;
218 free(buffer);
219 }
220#elif defined(__FreeBSD__)
221 {
222 int mib[4];
223 std::size_t bufSize = 1024;
224 char* buf = (char*)malloc(bufSize);
225
226 mib[0] = CTL_KERN;
227 mib[1] = KERN_PROC;
228 mib[2] = KERN_PROC_PATHNAME;
229 mib[3] = -1;
230
231 sysctl(mib, 4, buf, &bufSize, nullptr, 0);
232 filename = buf;
233 }
234#endif
235 return Dirname(filename);
236}
237
238std::string
239Append(std::string left, std::string right)
240{
241 // removing trailing separators from 'left'
242 NS_LOG_FUNCTION(left << right);
243 while (true)
244 {
245 std::string::size_type lastSep = left.rfind(SYSTEM_PATH_SEP);
246 if (lastSep != left.size() - 1)
247 {
248 break;
249 }
250 left = left.substr(0, left.size() - 1);
251 }
252 std::string retval = left + SYSTEM_PATH_SEP + right;
253 return retval;
254}
255
256std::list<std::string>
257Split(std::string path)
258{
260 std::list<std::string> retval;
261 std::string::size_type current = 0;
262 std::string::size_type next = 0;
263 next = path.find(SYSTEM_PATH_SEP, current);
264 while (next != std::string::npos)
265 {
266 std::string item = path.substr(current, next - current);
267 retval.push_back(item);
268 current = next + 1;
269 next = path.find(SYSTEM_PATH_SEP, current);
270 }
271 std::string item = path.substr(current, next - current);
272 retval.push_back(item);
273 return retval;
274}
275
276std::string
277Join(std::list<std::string>::const_iterator begin, std::list<std::string>::const_iterator end)
278{
279 NS_LOG_FUNCTION(*begin << *end);
280 std::string retval = "";
281 for (std::list<std::string>::const_iterator i = begin; i != end; i++)
282 {
283 if (*i == "")
284 {
285 // skip empty strings in the path list
286 continue;
287 }
288 else if (i == begin)
289 {
290 retval = *i;
291 }
292 else
293 {
294 retval = retval + SYSTEM_PATH_SEP + *i;
295 }
296 }
297 return retval;
298}
299
300std::list<std::string>
301ReadFiles(std::string path)
302{
304 bool err;
305 std::list<std::string> files;
306 std::tie(files, err) = ReadFilesNoThrow(path);
307 if (err)
308 {
309 NS_FATAL_ERROR("Could not open directory=" << path);
310 }
311 return files;
312}
313
314std::string
316{
318 char* path = nullptr;
319
320 path = std::getenv("TMP");
321 if (!path || std::strlen(path) == 0)
322 {
323 path = std::getenv("TEMP");
324 if (!path || std::strlen(path) == 0)
325 {
326 path = const_cast<char*>("/tmp");
327 }
328 }
329
330 //
331 // Just in case the user wants to go back and find the output, we give
332 // a hint as to which dir we created by including a time hint.
333 //
334 time_t now = time(nullptr);
335 struct tm* tm_now = localtime(&now);
336 //
337 // But we also randomize the name in case there are multiple users doing
338 // this at the same time
339 //
340 srand(time(nullptr));
341 long int n = rand();
342
343 //
344 // The final path to the directory is going to look something like
345 //
346 // /tmp/ns3.14.30.29.32767
347 //
348 // The first segment comes from one of the temporary directory env
349 // variables or /tmp if not found. The directory name starts with an
350 // identifier telling folks who is making all of the temp directories
351 // and then the local time (in this case 14.30.29 -- which is 2:30 and
352 // 29 seconds PM).
353 //
354 std::ostringstream oss;
355 oss << path << SYSTEM_PATH_SEP << "ns-3." << tm_now->tm_hour << "." << tm_now->tm_min << "."
356 << tm_now->tm_sec << "." << n;
357
358 return oss.str();
359}
360
361void
363{
365
366 std::error_code ec;
367 if (!fs::exists(path))
368 {
369 fs::create_directories(path, ec);
370 }
371
372 if (ec.value())
373 {
374 NS_FATAL_ERROR("failed creating directory " << path);
375 }
376}
377
378bool
379Exists(const std::string path)
380{
382
383 bool err;
384 auto dirpath = Dirname(path);
385 std::list<std::string> files;
386 tie(files, err) = ReadFilesNoThrow(dirpath);
387 if (err)
388 {
389 // Directory doesn't exist
390 NS_LOG_LOGIC("directory doesn't exist: " << dirpath);
391 return false;
392 }
393 NS_LOG_LOGIC("directory exists: " << dirpath);
394
395 // Check if the file itself exists
396 auto tokens = Split(path);
397 std::string file = tokens.back();
398
399 if (file == "")
400 {
401 // Last component was a directory, not a file name
402 // We already checked that the directory exists,
403 // so return true
404 NS_LOG_LOGIC("directory path exists: " << path);
405 return true;
406 }
407
408 files = ReadFiles(dirpath);
409
410 auto it = std::find(files.begin(), files.end(), file);
411 if (it == files.end())
412 {
413 // File itself doesn't exist
414 NS_LOG_LOGIC("file itself doesn't exist: " << file);
415 return false;
416 }
417
418 NS_LOG_LOGIC("file itself exists: " << file);
419 return true;
420
421} // Exists()
422
423std::string
424CreateValidSystemPath(const std::string path)
425{
426 // Windows and its file systems, e.g. NTFS and (ex)FAT(12|16|32),
427 // do not like paths with empty spaces or special symbols.
428 // Some of these symbols are allowed in test names, checked in TestCase::AddTestCase.
429 // We replace them with underlines to ensure they work on Windows.
430 std::regex incompatible_characters(" |:[^\\\\]|<|>|\\*");
431 std::string valid_path;
432 std::regex_replace(std::back_inserter(valid_path),
433 path.begin(),
434 path.end(),
435 incompatible_characters,
436 "_");
437 return valid_path;
438} // CreateValidSystemPath
439
440} // namespace SystemPath
441
442} // namespace ns3
NS_ASSERT() and NS_ASSERT_MSG() macro definitions.
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:160
#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:301
bool Exists(const std::string path)
Check if a path exists.
Definition: system-path.cc:379
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:257
std::string Dirname(std::string path)
Get the directory path for a file.
Definition: system-path.cc:139
void MakeDirectories(std::string path)
Create all the directories leading to path.
Definition: system-path.cc:362
std::string MakeTemporaryDirectoryName()
Get the name of a temporary directory.
Definition: system-path.cc:315
std::string Append(std::string left, std::string right)
Join two file system path elements.
Definition: system-path.cc:239
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:277
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:424
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:105
std::string FindSelfDirectory()
Get the file system path to the current executable.
Definition: system-path.cc:149
Debug message logging.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
constexpr auto SYSTEM_PATH_SEP
System-specific path separator used between directory names.
Definition: system-path.cc:79
ns3::SystemPath declarations.