/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2016-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see .
Description
POSIX versions of the functions declared in OSspecific.H
\*---------------------------------------------------------------------------*/
#if defined(__sun__) && defined(__GNUC__)
// Not certain if this is still required
#define _SYS_VNODE_H
#endif
#include "OSspecific.H"
#include "POSIX.H"
#include "fileName.H"
#include "fileStat.H"
#include "timer.H"
#include "DynamicList.H"
#include "CStringList.H"
#include "stringOps.H"
#include "IOstreams.H"
#include "Pstream.H"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef __APPLE__
#define EXT_SO "dylib"
#include
#else
#define EXT_SO "so"
// PGI does not have __int128_t
#ifdef __PGIC__
#define __ILP32__
#endif
#include
#endif
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(POSIX, 0);
}
static bool cwdPreference_(Foam::debug::optimisationSwitch("cwd", 0));
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
// After a fork in system(), before the exec() do the following
// - close stdin when executing in background (daemon-like)
// - redirect stdout to stderr when infoDetailLevel == 0
static inline void redirects(const bool bg)
{
if (bg)
{
// Close stdin(0) - unchecked return value
(void) ::close(STDIN_FILENO);
}
// Redirect stdout(1) to stderr(2) '1>&2'
if (Foam::infoDetailLevel == 0)
{
// This is correct. 1>&2 means dup2(2, 1);
(void) ::dup2(STDERR_FILENO, STDOUT_FILENO);
}
}
// Library loading is normally simply via dlopen(),
// but SIP (System Integrity Protection) on Apple will generally
// clear out the DYLD_LIBRARY_PATH set from shell scripts.
// We thus have FOAM_LD_LIBRARY_PATH as a shadow parameter and use
// that to attempt loading ourselves
static inline void* loadLibrary(const Foam::fileName& libName)
{
constexpr int ldflags = (RTLD_LAZY|RTLD_GLOBAL);
#ifdef __APPLE__
using namespace Foam;
const char* normal = nullptr;
const char* shadow = nullptr;
if
(
!libName.isAbsolute()
&& ((normal = ::getenv("DYLD_LIBRARY_PATH")) == nullptr || !*normal)
&& ((shadow = ::getenv("FOAM_LD_LIBRARY_PATH")) != nullptr && *shadow)
)
{
// SIP appears to have cleared DYLD_LIBRARY_PATH but the
// shadow parameter is available
const std::string ldPaths(shadow);
const auto paths = Foam::stringOps::split(ldPaths, ':');
for (const auto& p : paths)
{
if (p.length()) // Split removes empty, but be paranoid
{
const Foam::fileName fullPath(p.str()/libName);
void* handle = ::dlopen(fullPath.c_str(), ldflags);
if (handle)
{
return handle;
}
}
}
}
#endif
// Regular loading
return ::dlopen(libName.c_str(), ldflags);
}
// * * * * * * * * * * * * * * * * Local Classes * * * * * * * * * * * * * * //
namespace Foam
{
namespace POSIX
{
//- A simple directory contents iterator
class directoryIterator
{
DIR* dirptr_;
bool exists_;
bool hidden_;
std::string item_;
//- Accept file/dir name
inline bool accept() const
{
return
(
item_.size() && item_ != "." && item_ != ".."
&& (hidden_ || item_[0] != '.')
);
}
public:
// Constructors
//- Construct for dirName, optionally allowing hidden files/dirs
directoryIterator(const std::string& dirName, bool allowHidden = false)
:
dirptr_(nullptr),
exists_(false),
hidden_(allowHidden),
item_()
{
if (!dirName.empty())
{
dirptr_ = ::opendir(dirName.c_str());
exists_ = (dirptr_ != nullptr);
next(); // Move to first element
}
}
//- Destructor
~directoryIterator()
{
close();
}
// Member Functions
//- Directory open succeeded
bool exists() const noexcept
{
return exists_;
}
//- Directory pointer is valid
bool good() const noexcept
{
return dirptr_;
}
//- Close directory
void close()
{
if (dirptr_)
{
::closedir(dirptr_);
dirptr_ = nullptr;
}
}
//- The current item
const std::string& val() const noexcept
{
return item_;
}
//- Read next item, always ignoring "." and ".." entries.
// Normally also ignore hidden files/dirs (beginning with '.')
// Automatically close when there are no more items
bool next()
{
struct dirent *list;
while (dirptr_ && (list = ::readdir(dirptr_)) != nullptr)
{
item_ = list->d_name;
if (accept())
{
return true;
}
}
close(); // No more items
return false;
}
// Member Operators
//- Same as good()
operator bool() const noexcept
{
return good();
}
//- Same as val()
const std::string& operator*() const noexcept
{
return val();
}
//- Same as next()
directoryIterator& operator++()
{
next();
return *this;
}
};
} // End namespace POSIX
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
pid_t Foam::pid()
{
return ::getpid();
}
pid_t Foam::ppid()
{
return ::getppid();
}
pid_t Foam::pgid()
{
return ::getpgrp();
}
bool Foam::hasEnv(const std::string& envName)
{
// An empty envName => always false
return !envName.empty() && ::getenv(envName.c_str()) != nullptr;
}
Foam::string Foam::getEnv(const std::string& envName)
{
// Ignore an empty envName => always ""
char* env = envName.empty() ? nullptr : ::getenv(envName.c_str());
if (env)
{
return string(env);
}
// Return null-constructed string rather than string::null
// to avoid cyclic dependencies in the construction of globals
return string();
}
bool Foam::setEnv
(
const word& envName,
const std::string& value,
const bool overwrite
)
{
// Ignore an empty envName => always false
return
(
!envName.empty()
&& ::setenv(envName.c_str(), value.c_str(), overwrite) == 0
);
}
Foam::string Foam::hostName()
{
char buf[128];
::gethostname(buf, sizeof(buf));
return buf;
}
// DEPRECATED (2022-01)
Foam::string Foam::hostName(bool full)
{
// implementation as per hostname from net-tools
if (full)
{
char buf[128];
::gethostname(buf, sizeof(buf));
struct hostent *hp = ::gethostbyname(buf);
if (hp)
{
return hp->h_name;
}
return buf;
}
return Foam::hostName();
}
// DEPRECATED (2022-01)
Foam::string Foam::domainName()
{
char buf[128];
::gethostname(buf, sizeof(buf));
// implementation as per hostname from net-tools
struct hostent *hp = ::gethostbyname(buf);
if (hp)
{
char *p = ::strchr(hp->h_name, '.');
if (p)
{
++p;
return p;
}
}
return string();
}
Foam::string Foam::userName()
{
struct passwd* pw = ::getpwuid(::getuid());
if (pw != nullptr)
{
return pw->pw_name;
}
return string();
}
bool Foam::isAdministrator()
{
return (::geteuid() == 0);
}
Foam::fileName Foam::home()
{
char* env = ::getenv("HOME");
if (env)
{
return fileName(env);
}
struct passwd* pw = ::getpwuid(::getuid());
if (pw)
{
return pw->pw_dir;
}
return fileName();
}
Foam::fileName Foam::home(const std::string& userName)
{
// An empty userName => same as home()
if (userName.empty())
{
return Foam::home();
}
struct passwd* pw = ::getpwnam(userName.c_str());
if (pw)
{
return pw->pw_dir;
}
return fileName();
}
namespace Foam
{
//- The physical current working directory path name (pwd -P).
static Foam::fileName cwd_P()
{
label pathLengthLimit = POSIX::pathLengthChunk;
List path(pathLengthLimit);
// Resize path if getcwd fails with an ERANGE error
while (pathLengthLimit == path.size())
{
if (::getcwd(path.data(), path.size()))
{
return path.data();
}
else if (errno == ERANGE)
{
// Increment path length up to the pathLengthMax limit
if
(
(pathLengthLimit += POSIX::pathLengthChunk)
>= POSIX::pathLengthMax
)
{
FatalErrorInFunction
<< "Attempt to increase path length beyond limit of "
<< POSIX::pathLengthMax
<< exit(FatalError);
}
path.resize(pathLengthLimit);
}
else
{
break;
}
}
FatalErrorInFunction
<< "Couldn't get the current working directory"
<< exit(FatalError);
return fileName();
}
//- The logical current working directory path name.
// From the PWD environment, same as pwd -L.
static Foam::fileName cwd_L()
{
const char* env = ::getenv("PWD");
// Basic check
if (!env || env[0] != '/')
{
WarningInFunction
<< "PWD is invalid - reverting to physical description"
<< nl;
return cwd_P();
}
fileName dir(env);
// Check for "/."
for
(
std::string::size_type pos = 0;
std::string::npos != (pos = dir.find("/.", pos));
/*nil*/
)
{
pos += 2;
if
(
// Ends in "/." or has "/./"
!dir[pos] || dir[pos] == '/'
// Ends in "/.." or has "/../"
|| (dir[pos] == '.' && (!dir[pos+1] || dir[pos+1] == '/'))
)
{
WarningInFunction
<< "PWD contains /. or /.. - reverting to physical description"
<< nl;
return cwd_P();
}
}
// Finally, verify that PWD actually corresponds to the "." directory
if (!fileStat(dir, true).sameINode(fileStat(".", true)))
{
WarningInFunction
<< "PWD is not the cwd() - reverting to physical description"
<< nl;
return cwd_P();
}
return fileName(dir);
}
} // End namespace Foam
Foam::fileName Foam::cwd()
{
return cwd(cwdPreference_);
}
Foam::fileName Foam::cwd(bool logical)
{
if (logical)
{
return cwd_L();
}
return cwd_P();
}
bool Foam::chDir(const fileName& dir)
{
// Ignore an empty dir name => always false
return !dir.empty() && ::chdir(dir.c_str()) == 0;
}
bool Foam::mkDir(const fileName& pathName, mode_t mode)
{
if (POSIX::debug)
{
Pout<< FUNCTION_NAME << " : pathName:" << pathName << " mode:" << mode
<< endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// empty names are meaningless
if (pathName.empty())
{
return false;
}
// Construct path directory if does not exist
if (::mkdir(pathName.c_str(), mode) == 0)
{
// Directory made OK so return true
return true;
}
switch (errno)
{
case EPERM:
{
FatalErrorInFunction
<< "The filesystem containing " << pathName
<< " does not support the creation of directories."
<< exit(FatalError);
break;
}
case EEXIST:
{
// Directory already exists so simply return true
return true;
}
case EFAULT:
{
FatalErrorInFunction
<< "" << pathName
<< " points outside your accessible address space."
<< exit(FatalError);
break;
}
case EACCES:
{
FatalErrorInFunction
<< "The parent directory does not allow write "
"permission to the process,"<< nl
<< " or one of the directories in " << pathName
<< " did not allow search (execute) permission."
<< exit(FatalError);
break;
}
case ENAMETOOLONG:
{
FatalErrorInFunction
<< "" << pathName << " is too long."
<< exit(FatalError);
break;
}
case ENOENT:
{
// Part of the path does not exist so try to create it
if (pathName.path().size() && mkDir(pathName.path(), mode))
{
return mkDir(pathName, mode);
}
FatalErrorInFunction
<< "Couldn't create directory " << pathName
<< exit(FatalError);
break;
}
case ENOTDIR:
{
FatalErrorInFunction
<< "A component used as a directory in " << pathName
<< " is not, in fact, a directory."
<< exit(FatalError);
break;
}
case ENOMEM:
{
FatalErrorInFunction
<< "Insufficient kernel memory was available to make directory "
<< pathName << '.'
<< exit(FatalError);
break;
}
case EROFS:
{
FatalErrorInFunction
<< "" << pathName
<< " refers to a file on a read-only filesystem."
<< exit(FatalError);
break;
}
case ELOOP:
{
FatalErrorInFunction
<< "Too many symbolic links were encountered in resolving "
<< pathName << '.'
<< exit(FatalError);
break;
}
case ENOSPC:
{
FatalErrorInFunction
<< "The device containing " << pathName
<< " has no room for the new directory or "
<< "the user's disk quota is exhausted."
<< exit(FatalError);
break;
}
default:
{
FatalErrorInFunction
<< "Couldn't create directory " << pathName
<< exit(FatalError);
break;
}
}
return false;
}
bool Foam::chMod(const fileName& name, const mode_t m)
{
if (POSIX::debug)
{
Pout<< FUNCTION_NAME << " : name:" << name << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// Ignore an empty name => always false
return !name.empty() && ::chmod(name.c_str(), m) == 0;
}
mode_t Foam::mode(const fileName& name, const bool followLink)
{
if (POSIX::debug)
{
Pout<< FUNCTION_NAME << " : name:" << name << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// Ignore an empty name => always 0
if (!name.empty())
{
fileStat fileStatus(name, followLink);
if (fileStatus.good())
{
return fileStatus.status().st_mode;
}
}
return 0;
}
Foam::fileName::Type Foam::type
(
const fileName& name,
const bool followLink
)
{
// Ignore an empty name => always UNDEFINED
if (name.empty())
{
return fileName::Type::UNDEFINED;
}
if (POSIX::debug)
{
Pout<< FUNCTION_NAME << " : name:" << name << endl;
}
mode_t m = mode(name, followLink);
if (S_ISREG(m))
{
return fileName::Type::FILE;
}
else if (S_ISLNK(m))
{
return fileName::Type::SYMLINK;
}
else if (S_ISDIR(m))
{
return fileName::Type::DIRECTORY;
}
return fileName::Type::UNDEFINED;
}
bool Foam::exists
(
const fileName& name,
const bool checkGzip,
const bool followLink
)
{
if (POSIX::debug)
{
Pout<< FUNCTION_NAME << " : name:" << name << " checkGzip:" << checkGzip
<< endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// Ignore an empty name => always false
return
(
!name.empty()
&& (mode(name, followLink) || isFile(name, checkGzip, followLink))
);
}
bool Foam::isDir(const fileName& name, const bool followLink)
{
if (POSIX::debug)
{
Pout<< FUNCTION_NAME << " : name:" << name << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// Ignore an empty name => always false
return !name.empty() && S_ISDIR(mode(name, followLink));
}
bool Foam::isFile
(
const fileName& name,
const bool checkGzip,
const bool followLink
)
{
if (POSIX::debug)
{
Pout<< FUNCTION_NAME << " : name:" << name << " checkGzip:" << checkGzip
<< endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// Ignore an empty name => always false
return
(
!name.empty()
&& (
S_ISREG(mode(name, followLink))
|| (checkGzip && S_ISREG(mode(name + ".gz", followLink)))
)
);
}
off_t Foam::fileSize(const fileName& name, const bool followLink)
{
if (POSIX::debug)
{
Pout<< FUNCTION_NAME << " : name:" << name << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// Ignore an empty name
if (!name.empty())
{
fileStat fileStatus(name, followLink);
if (fileStatus.good())
{
return fileStatus.status().st_size;
}
}
return -1;
}
time_t Foam::lastModified(const fileName& name, const bool followLink)
{
if (POSIX::debug)
{
Pout<< FUNCTION_NAME << " : name:" << name << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// Ignore an empty name
return name.empty() ? 0 : fileStat(name, followLink).modTime();
}
double Foam::highResLastModified(const fileName& name, const bool followLink)
{
if (POSIX::debug)
{
Pout<< FUNCTION_NAME << " : name:" << name << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// Ignore an empty name
return name.empty() ? 0 : fileStat(name, followLink).dmodTime();
}
Foam::fileNameList Foam::readDir
(
const fileName& directory,
const fileName::Type type,
const bool filtergz,
const bool followLink
)
{
// Initial filename list size and the increment when resizing the list
constexpr int maxNnames = 100;
fileNameList dirEntries;
// Iterate contents (ignores an empty directory name)
POSIX::directoryIterator dirIter(directory);
if (!dirIter.exists())
{
if (POSIX::debug)
{
InfoInFunction
<< "cannot open directory " << directory << endl;
}
return dirEntries;
}
if (POSIX::debug)
{
// InfoInFunction
Pout<< FUNCTION_NAME << " : reading directory " << directory << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
label nFailed = 0; // Entries with invalid characters
label nEntries = 0; // Number of selected entries
dirEntries.resize(maxNnames);
// Process the directory entries
for (/*nil*/; dirIter; ++dirIter)
{
const std::string& item = *dirIter;
// Validate filename without spaces, quotes, etc in the name.
// No duplicate slashes to strip - dirent will not have them anyhow.
fileName name(fileName::validate(item));
if (name != item)
{
++nFailed;
}
else if
(
(type == fileName::Type::DIRECTORY)
|| (type == fileName::Type::FILE && !fileName::isBackup(name))
)
{
fileName::Type detected = (directory/name).type(followLink);
if (detected == type)
{
// Only strip '.gz' from non-directory names
if
(
filtergz
&& (detected != fileName::Type::DIRECTORY)
&& name.has_ext("gz")
)
{
name.remove_ext();
}
if (nEntries >= dirEntries.size())
{
dirEntries.resize(dirEntries.size() + maxNnames);
}
dirEntries[nEntries] = std::move(name);
++nEntries;
}
}
}
// Finalize the length of the entries list
dirEntries.resize(nEntries);
if (nFailed && POSIX::debug)
{
std::cerr
<< "Foam::readDir() : reading directory " << directory << nl
<< nFailed << " entries with invalid characters in their name"
<< std::endl;
}
return dirEntries;
}
bool Foam::cp(const fileName& src, const fileName& dest, const bool followLink)
{
if (POSIX::debug)
{
Pout<< FUNCTION_NAME << " : src:" << src << " dest:" << dest << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// Make sure source exists - this also handles an empty source name
if (!exists(src))
{
return false;
}
const fileName::Type srcType = src.type(followLink);
fileName destFile(dest);
// Check type of source file.
if (srcType == fileName::FILE)
{
// If dest is a directory, create the destination file name.
if (destFile.type() == fileName::DIRECTORY)
{
destFile /= src.name();
}
// Make sure the destination directory exists.
if (!isDir(destFile.path()) && !mkDir(destFile.path()))
{
return false;
}
// Open and check streams. Enforce binary for extra safety
std::ifstream srcStream(src, ios_base::in | ios_base::binary);
if (!srcStream)
{
return false;
}
std::ofstream destStream(destFile, ios_base::out | ios_base::binary);
if (!destStream)
{
return false;
}
// Copy character data.
char ch;
while (srcStream.get(ch))
{
destStream.put(ch);
}
// Final check.
if (!srcStream.eof() || !destStream)
{
return false;
}
}
else if (srcType == fileName::SYMLINK)
{
// If dest is a directory, create the destination file name.
if (destFile.type() == fileName::DIRECTORY)
{
destFile /= src.name();
}
// Make sure the destination directory exists.
if (!isDir(destFile.path()) && !mkDir(destFile.path()))
{
return false;
}
Foam::ln(src, destFile);
}
else if (srcType == fileName::DIRECTORY)
{
if (destFile.type() == fileName::DIRECTORY)
{
// Both are directories. Could mean copy contents or copy
// recursively. Don't actually know what the user wants,
// but assume that if names are identical == copy contents.
//
// So: "path1/foo" "path2/foo" copy contents
// So: "path1/foo" "path2/bar" copy directory
const word srcDirName = src.name();
if (destFile.name() != srcDirName)
{
destFile /= srcDirName;
}
}
// Make sure the destination directory exists.
if (!isDir(destFile) && !mkDir(destFile))
{
return false;
}
char* realSrcPath = realpath(src.c_str(), nullptr);
char* realDestPath = realpath(destFile.c_str(), nullptr);
const bool samePath = strcmp(realSrcPath, realDestPath) == 0;
if (POSIX::debug && samePath)
{
InfoInFunction
<< "Attempt to copy " << realSrcPath << " to itself" << endl;
}
if (realSrcPath)
{
free(realSrcPath);
}
if (realDestPath)
{
free(realDestPath);
}
// Do not copy over self when src is actually a link to dest
if (samePath)
{
return false;
}
// Copy files
fileNameList files = readDir(src, fileName::FILE, false, followLink);
for (const fileName& item : files)
{
if (POSIX::debug)
{
InfoInFunction
<< "Copying : " << src/item
<< " to " << destFile/item << endl;
}
// File to file.
Foam::cp(src/item, destFile/item, followLink);
}
// Copy sub directories.
fileNameList dirs = readDir
(
src,
fileName::DIRECTORY,
false,
followLink
);
for (const fileName& item : dirs)
{
if (POSIX::debug)
{
InfoInFunction
<< "Copying : " << src/item
<< " to " << destFile << endl;
}
// Dir to Dir.
Foam::cp(src/item, destFile, followLink);
}
}
else
{
return false;
}
return true;
}
bool Foam::ln(const fileName& src, const fileName& dst)
{
if (POSIX::debug)
{
//InfoInFunction
Pout<< FUNCTION_NAME
<< " : Create symlink from : " << src << " to " << dst << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
if (src.empty())
{
WarningInFunction
<< "source name is empty: not linking." << endl;
return false;
}
if (dst.empty())
{
WarningInFunction
<< "destination name is empty: not linking." << endl;
return false;
}
if (exists(dst))
{
WarningInFunction
<< "destination " << dst << " already exists. Not linking."
<< endl;
return false;
}
if (src.isAbsolute() && !exists(src))
{
WarningInFunction
<< "source " << src << " does not exist." << endl;
return false;
}
if (::symlink(src.c_str(), dst.c_str()) == 0)
{
return true;
}
WarningInFunction
<< "symlink from " << src << " to " << dst << " failed." << endl;
return false;
}
Foam::fileName Foam::readLink(const fileName& link)
{
if (POSIX::debug)
{
//InfoInFunction
Pout<< FUNCTION_NAME
<< " : Returning symlink destination for : " << link << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
if (link.empty())
{
// Treat an empty path as a no-op.
return fileName();
}
fileName result;
result.resize(1024); // Should be large enough (mostly relative anyhow)
ssize_t len = ::readlink(link.c_str(), &(result.front()), result.size());
if (len > 0)
{
result.resize(len);
return result;
}
// Failure: return empty result
return fileName();
}
bool Foam::mv(const fileName& src, const fileName& dst, const bool followLink)
{
if (POSIX::debug)
{
//InfoInFunction
Pout<< FUNCTION_NAME << " : Move : " << src << " to " << dst << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// Ignore empty names => always false
if (src.empty() || dst.empty())
{
return false;
}
if
(
dst.type() == fileName::DIRECTORY
&& src.type(followLink) != fileName::DIRECTORY
)
{
const fileName dstName(dst/src.name());
return (0 == std::rename(src.c_str(), dstName.c_str()));
}
return (0 == std::rename(src.c_str(), dst.c_str()));
}
bool Foam::mvBak(const fileName& src, const std::string& ext)
{
if (POSIX::debug)
{
//InfoInFunction
Pout<< FUNCTION_NAME
<< " : moving : " << src << " to extension " << ext << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// Ignore an empty name or extension => always false
if (src.empty() || ext.empty())
{
return false;
}
if (exists(src, false))
{
constexpr const int maxIndex = 99;
char index[4];
for (int n = 0; n <= maxIndex; ++n)
{
fileName dstName(src + "." + ext);
if (n)
{
::snprintf(index, 4, "%02d", n);
dstName += index;
}
// avoid overwriting existing files, except for the last
// possible index where we have no choice
if (!exists(dstName, false) || n == maxIndex)
{
return (0 == std::rename(src.c_str(), dstName.c_str()));
}
}
}
// fallthrough: nothing to do
return false;
}
bool Foam::rm(const fileName& file)
{
if (POSIX::debug)
{
//InfoInFunction
Pout<< FUNCTION_NAME << " : Removing : " << file << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// Ignore an empty name => always false
if (file.empty())
{
return false;
}
// If removal of plain file name fails, try with .gz
return
(
0 == ::remove(file.c_str())
|| 0 == ::remove((file + ".gz").c_str())
);
}
bool Foam::rmDir
(
const fileName& directory,
const bool silent,
const bool emptyOnly
)
{
if (directory.empty())
{
return false;
}
// Iterate contents (ignores an empty directory name)
// Also retain hidden files/dirs for removal
POSIX::directoryIterator dirIter(directory, true);
if (!dirIter.exists())
{
if (!silent && !emptyOnly)
{
WarningInFunction
<< "Cannot open directory " << directory << endl;
}
return false;
}
if (POSIX::debug)
{
//InfoInFunction
Pout<< FUNCTION_NAME << " : removing directory " << directory << endl;
if ((POSIX::debug & 2) && !Pstream::master())
{
error::printStack(Pout);
}
}
// Process each directory entry, counting any errors encountered
int nErrors = 0;
for (/*nil*/; dirIter; ++dirIter)
{
const std::string& item = *dirIter;
// Allow invalid characters (spaces, quotes, etc),
// otherwise we cannot remove subdirs with these types of names.
// -> const fileName path = directory/name; <-
const fileName path(fileName::concat(directory, item));
fileName::Type detected = path.type(false); // No followLink
if (detected == fileName::Type::DIRECTORY)
{
// Call silently for lower levels
if (!rmDir(path, true, emptyOnly))
{
++nErrors;
}
}
else if (emptyOnly)
{
// Only remove empty directories (not files)
++nErrors;
// Check for dead symlinks
if (detected == fileName::Type::SYMLINK)
{
detected = path.type(true); // followLink
if (detected == fileName::Type::UNDEFINED)
{
--nErrors;
if (!rm(path))
{
++nErrors;
}
}
}
}
else
{
if (!rm(path))
{
++nErrors;
}
}
}
if (nErrors == 0)
{
// No errors encountered - try to remove the top-level
if (!rm(directory))
{
nErrors = -1; // A top-level error
}
}
if (nErrors && !silent && !emptyOnly)
{
WarningInFunction
<< "Failed to remove directory " << directory << endl;
if (nErrors > 0)
{
Info<< "could not remove " << nErrors << " sub-entries" << endl;
}
}
return (nErrors == 0);
}
unsigned int Foam::sleep(const unsigned int sec)
{
return ::sleep(sec);
}
void Foam::fdClose(const int fd)
{
if (close(fd) != 0)
{
FatalErrorInFunction
<< "close error on " << fd << endl
<< abort(FatalError);
}
}
bool Foam::ping
(
const std::string& destName,
const label destPort,
const label timeOut
)
{
struct hostent *hostPtr;
volatile int sockfd;
struct sockaddr_in destAddr; // will hold the destination addr
u_int addr;
if ((hostPtr = ::gethostbyname(destName.c_str())) == nullptr)
{
FatalErrorInFunction
<< "gethostbyname error " << h_errno << " for host " << destName
<< abort(FatalError);
}
// Get first of the SLL of addresses
addr = (reinterpret_cast(*(hostPtr->h_addr_list)))->s_addr;
// Allocate socket
sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
FatalErrorInFunction
<< "socket error"
<< abort(FatalError);
}
// Fill sockaddr_in structure with dest address and port
std::memset(reinterpret_cast(&destAddr), '\0', sizeof(destAddr));
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(ushort(destPort));
destAddr.sin_addr.s_addr = addr;
timer myTimer(timeOut);
if (timedOut(myTimer))
{
// Setjmp from timer jumps back to here
fdClose(sockfd);
return false;
}
if
(
::connect
(
sockfd,
reinterpret_cast(&destAddr),
sizeof(struct sockaddr)
) != 0
)
{
// Connection refused. Check if network was actually used or not.
int connectErr = errno;
fdClose(sockfd);
if (connectErr == ECONNREFUSED)
{
return true;
}
//perror("connect");
return false;
}
fdClose(sockfd);
return true;
}
bool Foam::ping(const std::string& host, const label timeOut)
{
return ping(host, 222, timeOut) || ping(host, 22, timeOut);
}
namespace Foam
{
//! \cond fileScope
static int waitpid(const pid_t pid)
{
// child status, return code from the exec etc.
int status = 0;
// in parent - blocking wait
// modest treatment of signals (in child)
// treat 'stopped' like exit (suspend/continue)
while (true)
{
pid_t wpid = ::waitpid(pid, &status, WUNTRACED);
if (wpid == -1)
{
FatalErrorInFunction
<< "some error occurred in child"
<< exit(FatalError);
break;
}
if (WIFEXITED(status))
{
// child exited, get its return status
return WEXITSTATUS(status);
}
if (WIFSIGNALED(status))
{
// child terminated by some signal
return WTERMSIG(status);
}
if (WIFSTOPPED(status))
{
// child stopped by some signal
return WSTOPSIG(status);
}
FatalErrorInFunction
<< "programming error, status from waitpid() not handled: "
<< status
<< exit(FatalError);
}
return -1; // should not happen
}
//! \endcond
}
int Foam::system(const std::string& command, const bool bg)
{
if (command.empty())
{
// Treat an empty command as a successful no-op.
// From 'man sh' POSIX (man sh):
// "If the command_string operand is an empty string,
// sh shall exit with a zero exit status."
return 0;
}
// TBD: vfork is deprecated as of macOS 12.0
const pid_t child_pid = ::vfork(); // NB: vfork, not fork!
if (child_pid == -1)
{
FatalErrorInFunction
<< "vfork() failed for system command " << command
<< exit(FatalError);
return -1; // fallback error value
}
else if (child_pid == 0)
{
// In child
// Close or redirect file descriptors
redirects(bg);
// execl uses the current environ
(void) ::execl
(
"/bin/sh", // Path of the shell
"sh", // Command-name (name for the shell)
"-c", // Read commands from command_string operand
command.c_str(), // Command string
reinterpret_cast(0)
);
// Obviously failed, since exec should not return
FatalErrorInFunction
<< "exec failed: " << command
<< exit(FatalError);
return -1; // fallback error value
}
// In parent
// - started as background process, or blocking wait for the child
return (bg ? 0 : waitpid(child_pid));
}
int Foam::system(const CStringList& command, const bool bg)
{
if (command.empty())
{
// Treat an empty command as a successful no-op.
// For consistency with POSIX (man sh) behaviour for (sh -c command),
// which is what is mostly being replicated here.
return 0;
}
// NB: use vfork, not fork!
// vfork behaves more like a thread and avoids copy-on-write problems
// triggered by fork.
// The normal system() command has a fork buried in it that causes
// issues with infiniband and openmpi etc.
// TBD: vfork is deprecated as of macOS 12.0
const pid_t child_pid = ::vfork();
if (child_pid == -1)
{
FatalErrorInFunction
<< "vfork() failed for system command " << command[0]
<< exit(FatalError);
return -1; // fallback error value
}
else if (child_pid == 0)
{
// In child
// Close or redirect file descriptors
redirects(bg);
// execvp searches the path, uses the current environ
(void) ::execvp(command[0], command.strings());
// Obviously failed, since exec should not return
FatalErrorInFunction
<< "exec(" << command[0] << ", ...) failed"
<< exit(FatalError);
return -1; // fallback error value
}
// In parent
// - started as background process, or blocking wait for the child
return (bg ? 0 : waitpid(child_pid));
}
int Foam::system(const Foam::UList& command, const bool bg)
{
if (command.empty())
{
// Treat an empty command as a successful no-op.
return 0;
}
// Make a deep copy as C-strings
const CStringList cmd(command);
return Foam::system(cmd, bg);
}
void* Foam::dlOpen(const fileName& libName, const bool check)
{
if (POSIX::debug)
{
std::cout
<< "dlopen() of " << libName << std::endl;
}
void* handle = loadLibrary(libName);
if (!handle)
{
fileName libso;
if (!libName.has_path() && !libName.starts_with("lib"))
{
// Try with 'lib' prefix
libso = "lib" + libName;
handle = loadLibrary(libso);
if (POSIX::debug)
{
std::cout
<< " dlopen() as " << libso << std::endl;
}
}
else
{
libso = libName;
}
// With canonical library extension ("so" or "dylib"), which remaps
// "libXX" to "libXX.so" as well as "libXX.so" -> "libXX.dylib"
if (!handle && !libso.has_ext(EXT_SO))
{
libso.replace_ext(EXT_SO);
handle = loadLibrary(libso);
if (POSIX::debug)
{
std::cout
<< " dlopen() as " << libso << std::endl;
}
}
}
if (!handle && check)
{
WarningInFunction
<< "dlopen error : " << ::dlerror() << endl;
}
if (POSIX::debug)
{
std::cout
<< "dlopen() of " << libName
<< " handle " << handle << std::endl;
}
return handle;
}
void* Foam::dlOpen(const fileName& libName, std::string& errorMsg)
{
// Call without emitting error message - we capture that ourselves
void* handle = Foam::dlOpen(libName, false);
if (!handle)
{
// Capture error message
errorMsg = ::dlerror();
}
else
{
// No errors
errorMsg.clear();
}
return handle;
}
Foam::label Foam::dlOpen
(
std::initializer_list libNames,
const bool check
)
{
label nLoaded = 0;
for (const fileName& libName : libNames)
{
if (Foam::dlOpen(libName, check))
{
++nLoaded;
}
}
return nLoaded;
}
bool Foam::dlClose(void* handle)
{
if (POSIX::debug)
{
std::cout
<< "dlClose(void*)"
<< " : dlclose of handle " << handle << std::endl;
}
return ::dlclose(handle) == 0;
}
void* Foam::dlSymFind(void* handle, const std::string& symbol, bool required)
{
if (!required && (!handle || symbol.empty()))
{
return nullptr;
}
if (POSIX::debug)
{
std::cout
<< "dlSymFind(void*, const std::string&, bool)"
<< " : dlsym of " << symbol << std::endl;
}
// Clear any old errors - see manpage dlopen
(void) ::dlerror();
// Get address of symbol
void* fun = ::dlsym(handle, symbol.c_str());
// Any error?
char *err = ::dlerror();
if (err)
{
if (!required)
{
return nullptr;
}
WarningInFunction
<< "Cannot lookup symbol " << symbol << " : " << err
<< endl;
}
return fun;
}
#ifndef __APPLE__
static int collectLibsCallback
(
struct dl_phdr_info *info,
size_t size,
void *data
)
{
Foam::DynamicList* ptr =
reinterpret_cast*>(data);
ptr->append(info->dlpi_name);
return 0;
}
#endif
Foam::fileNameList Foam::dlLoaded()
{
DynamicList libs;
#ifdef __APPLE__
for (uint32_t i=0; i < _dyld_image_count(); ++i)
{
libs.append(_dyld_get_image_name(i));
}
#else
dl_iterate_phdr(collectLibsCallback, &libs);
#endif
if (POSIX::debug)
{
std::cout
<< "dlLoaded()"
<< " : determined loaded libraries :" << libs.size() << std::endl;
}
return libs;
}
// ************************************************************************* //