Newer
Older
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation | Copyright (C) 2016-2018 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 <http://www.gnu.org/licenses/>.
POSIX versions of the functions declared in OSspecific.H
\*---------------------------------------------------------------------------*/
#ifdef solarisGcc
#define _SYS_VNODE_H
#endif
#include "OSspecific.H"
#include "foamVersion.H"
#include "fileName.H"
#include "fileStat.H"
#include "timer.H"
#include "IFstream.H"
#include "CStringList.H"
#include "SubList.H"
#include "IOstreams.H"
#include "Pstream.H"
#include <fstream>
#include <cstdlib>
#include <cctype>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <pwd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef darwin
#include <mach-o/dyld.h>
#else
#include <link.h>
#endif
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(POSIX, 0);
}
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
// Like fileName "/" global operator, but retain any invalid characters
static inline Foam::fileName fileNameConcat
(
const std::string& a,
const std::string& b
)
{
if (a.size())
{
if (b.size())
{
// Two non-empty strings: can concatenate
return Foam::fileName((a + '/' + b), false);
}
return Foam::fileName(a, false);
}
// Or, if the first string is empty
if (b.size())
{
return Foam::fileName(b, false);
}
// Both strings are empty
return Foam::fileName();
}
// 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);
}
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
pid_t Foam::pid()
{
pid_t Foam::ppid()
{
pid_t Foam::pgid()
{
bool Foam::env(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(bool full)
char buf[128];
// implementation as per hostname from net-tools
if (full)
{
struct hostent *hp = ::gethostbyname(buf);
if (hp)
return hp->h_name;
}
}
return buf;
Foam::string Foam::domainName()
{
char buf[128];
// 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::null;
}
Foam::string Foam::userName()
struct passwd* pw = ::getpwuid(::getuid());
if (pw != nullptr)
{
return pw->pw_name;
}
return string();
bool Foam::isAdministrator()
{
Foam::fileName Foam::home()
{
{
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();
}
Foam::fileName Foam::cwd()
{
label pathLengthLimit = POSIX::pathLengthChunk;
List<char> 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.setSize(pathLengthLimit);
}
else
{
break;
}
FatalErrorInFunction
<< "Couldn't get the current working directory"
<< exit(FatalError);
return fileName::null;
}
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;
}
else
{
switch (errno)
{
case EPERM:
{
<< "The filesystem containing " << pathName
<< " does not support the creation of directories."
<< exit(FatalError);
return false;
}
case EEXIST:
{
// Directory already exists so simply return true
return true;
}
case EFAULT:
{
<< "" << pathName
<< " points outside your accessible address space."
<< exit(FatalError);
return false;
}
case EACCES:
{
<< "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);
return false;
}
case ENAMETOOLONG:
{
<< "" << pathName << " is too long."
<< exit(FatalError);
return false;
}
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);
}
else
{
<< "Couldn't create directory " << pathName
<< exit(FatalError);
return false;
}
}
case ENOTDIR:
{
<< "A component used as a directory in " << pathName
<< " is not, in fact, a directory."
<< exit(FatalError);
return false;
}
case ENOMEM:
{
<< "Insufficient kernel memory was available to make "
"directory " << pathName << '.'
<< exit(FatalError);
return false;
}
case EROFS:
{
<< "" << pathName
<< " refers to a file on a read-only filesystem."
<< exit(FatalError);
return false;
}
case ELOOP:
{
<< "Too many symbolic links were encountered in resolving "
<< pathName << '.'
<< exit(FatalError);
return false;
}
case ENOSPC:
{
<< "The device containing " << pathName
<< " has no room for the new directory or "
<< "the user's disk quota is exhausted."
<< exit(FatalError);
return false;
}
default:
{
<< "Couldn't create directory " << pathName
<< exit(FatalError);
return false;
}
}
}
}
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.isValid())
{
return fileStatus.status().st_mode;
}
Foam::fileName::Type Foam::type(const fileName& name, const bool followLink)
// Ignore an empty name => always UNDEFINED
if (name.empty())
{
return fileName::UNDEFINED;
}
if (POSIX::debug)
{
Pout<< FUNCTION_NAME << " : name:" << name << endl;
}
mode_t m = mode(name, followLink);
if (S_ISREG(m))
{
return fileName::FILE;
}
else if (S_ISLNK(m))
{
return fileName::LINK;
}
else if (S_ISDIR(m))
{
return fileName::DIRECTORY;
}
return fileName::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
(
!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.isValid())
{
return fileStatus.status().st_size;
}
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
static const int maxNnames = 100;
// Basic sanity: cannot strip '.gz' from directory names
const bool stripgz = filtergz && (type != fileName::DIRECTORY);
const word extgz("gz");
fileNameList dirEntries;
// Open directory and set the structure pointer
// Do not attempt to open an empty directory name
DIR *source;
if
(
directory.empty()
|| (source = ::opendir(directory.c_str())) == nullptr
)
{
if (POSIX::debug)
{
InfoInFunction
<< "cannot open directory " << directory << endl;
}
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.setSize(maxNnames);
// Read and parse all the entries in the directory
for (struct dirent *list; (list = ::readdir(source)) != nullptr; /*nil*/)
const std::string item(list->d_name);
// Ignore files/directories beginning with "."
// These are the ".", ".." directories and any hidden files/dirs
if (item.empty() || item[0] == '.')
// Validate filename without spaces, quotes, etc in the name.
// No duplicate slashes to strip - dirent will not have them anyhow.
const fileName name(fileName::validate(item));
if (name != item)
{
++nFailed;
}
else if
(
(type == fileName::DIRECTORY)
|| (type == fileName::FILE && !fileName::isBackup(name))
)
{
if ((directory/name).type(followLink) == type)
if (nEntries >= dirEntries.size())
{
dirEntries.setSize(dirEntries.size() + maxNnames);
}
if (stripgz && name.hasExt(extgz))
dirEntries[nEntries++] = name.lessExt();
}
else
{
dirEntries[nEntries++] = name;
::closedir(source);
// Finalize the length of the entries list
dirEntries.setSize(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.
std::ifstream srcStream(src);
if (!srcStream)
{
return false;
}
std::ofstream destStream(destFile);
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::LINK)
{
// 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;
}
ln(src, destFile);
}
else if (srcType == fileName::DIRECTORY)
{
// If dest is a directory, create the destination file name.
if (destFile.type() == fileName::DIRECTORY)
{
destFile /= src.components().last();
// Make sure the destination directory exists.
if (!isDir(destFile) && !mkDir(destFile))
{
return false;
}
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
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;
}
fileNameList files = readDir(src, fileName::FILE, false, followLink);
for (const fileName& item : files)
InfoInFunction
<< "Copying : " << src/item
<< " to " << destFile/item << endl;
}
// File to file.
cp(src/item, destFile/item, followLink);
}
// Copy sub directories.
fileNameList dirs = readDir
(
src,
fileName::DIRECTORY,
false,
followLink
);
for (const fileName& item : dirs)
InfoInFunction
<< "Copying : " << src/item
<< " to " << destFile << endl;
}
// Dir to Dir.
cp(src/item, destFile, followLink);
else
{
return false;
}
return true;
}
bool Foam::ln(const fileName& src, const fileName& dst)
//InfoInFunction
Pout<< FUNCTION_NAME
<< " : Create softlink 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;
}
<< "destination " << dst << " already exists. Not linking."
<< endl;
return false;
}
if (src.isAbsolute() && !exists(src))