Skip to content
Snippets Groups Projects
POSIX.C 36.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*---------------------------------------------------------------------------*\
      =========                 |
      \\      /  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
    
    #endif
    
    #include "OSspecific.H"
    
    #include "POSIX.H"
    
    #include "foamVersion.H"
    #include "fileName.H"
    #include "fileStat.H"
    #include "timer.H"
    
    mattijs's avatar
    mattijs committed
    #include "DynamicList.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/stat.h>
    #include <sys/socket.h>
    #include <netdb.h>
    
    #include <dlfcn.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()
    {
    
        return ::getpid();
    
        return ::getppid();
    
        return ::getpgrp();
    
    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());
    
    
        // 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,
    
        // Ignore an empty envName => always false
        return
        (
            !envName.empty()
         && ::setenv(envName.c_str(), value.c_str(), overwrite) == 0
        );
    
    Foam::string Foam::hostName(bool full)
    
        ::gethostname(buf, sizeof(buf));
    
        // implementation as per hostname from net-tools
    
            struct hostent *hp = ::gethostbyname(buf);
    
    Foam::string Foam::domainName()
    
        ::gethostname(buf, sizeof(buf));
    
    
        // implementation as per hostname from net-tools
    
        struct hostent *hp = ::gethostbyname(buf);
    
            char *p = ::strchr(hp->h_name, '.');
    
    Foam::string Foam::userName()
    
        struct passwd* pw = ::getpwuid(::getuid());
    
    bool Foam::isAdministrator()
    {
    
        return (::geteuid() == 0);
    
    Foam::fileName Foam::home()
    {
    
        char* env = ::getenv("HOME");
    
    
        struct passwd* pw = ::getpwuid(::getuid());
        if (pw)
        {
            return pw->pw_dir;
        }
    
    Foam::fileName Foam::home(const std::string& userName)
    
        // An empty userName => same as home()
        if (userName.empty())
    
        struct passwd* pw = ::getpwnam(userName.c_str());
        if (pw)
    
        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();
            }
    
                // 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
    
        // 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:
                {
    
                    FatalErrorInFunction
    
                        << "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:
                {
    
                    FatalErrorInFunction
    
                        << "" << pathName
                        << " points outside your accessible address space."
                        << exit(FatalError);
    
                    return false;
                }
    
                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);
    
                    return false;
                }
    
                case ENAMETOOLONG:
                {
    
                    FatalErrorInFunction
    
                        << "" << 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
                    {
    
                        FatalErrorInFunction
    
                            << "Couldn't create directory " << pathName
                            << exit(FatalError);
    
                        return false;
                    }
                }
    
                case ENOTDIR:
                {
    
                    FatalErrorInFunction
    
                        << "A component used as a directory in " << pathName
                        << " is not, in fact, a directory."
                        << exit(FatalError);
    
                    return false;
                }
    
                case ENOMEM:
                {
    
                    FatalErrorInFunction
    
                        << "Insufficient kernel memory was available to make "
                           "directory " << pathName << '.'
                        << exit(FatalError);
    
                    return false;
                }
    
                case EROFS:
                {
    
                    FatalErrorInFunction
    
                        << "" << pathName
                        << " refers to a file on a read-only filesystem."
                        << exit(FatalError);
    
                    return false;
                }
    
                case ELOOP:
                {
    
                    FatalErrorInFunction
    
                        << "Too many symbolic links were encountered in resolving "
                        << pathName << '.'
                        << exit(FatalError);
    
                    return false;
                }
    
                case ENOSPC:
                {
    
                    FatalErrorInFunction
    
                        << "The device containing " << pathName
                        << " has no room for the new directory or "
                        << "the user's disk quota is exhausted."
                        << exit(FatalError);
    
                    return false;
                }
    
                default:
                {
    
                    FatalErrorInFunction
    
                        << "Couldn't create directory " << pathName
                        << exit(FatalError);
    
                    return false;
                }
            }
        }
    }
    
    
    
    Mark Olesen's avatar
    Mark Olesen committed
    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.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");
    
    
        // 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;
            }
    
    
        if (POSIX::debug)
    
            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;
    
        // 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;
        }
    
    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);
    
    
        // Check type of source file.
    
        if (srcType == fileName::FILE)
    
        {
            // If dest is a directory, create the destination file name.
            if (destFile.type() == fileName::DIRECTORY)
            {
    
            }
    
            // Make sure the destination directory exists.
    
            if (!isDir(destFile.path()) && !mkDir(destFile.path()))
    
            {
                return false;
            }
    
            // Open and check streams.
    
            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)
            {
    
            }
    
            // 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))
    
            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)
    
                if (POSIX::debug)
    
                        << "Copying : " << src/item
                        << " to " << destFile/item << endl;
    
                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)
    
                        << "Copying : " << src/item
    
                        << " to " << destFile << endl;
                }
    
                // Dir to Dir.
    
                cp(src/item, destFile, followLink);
    
    bool Foam::ln(const fileName& src, const fileName& dst)
    
        if (POSIX::debug)
    
            //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;
        }
    
    
        if (exists(dst))
    
                << "destination " << dst << " already exists. Not linking."
    
        if (src.isAbsolute() && !exists(src))