Skip to content

Commit

Permalink
enh(ProcessRunner): does not detect launch errors #4482 (#4483)
Browse files Browse the repository at this point in the history
* enh(ProcessRunner): does not detect launch errors #4482

* enh(File): add absolutePath and existsAnywhere() #4482

* fix windows build and tsan fail

* fix tsan

* fix windows file tests

* comment out some CI env path -related issues

* fix tsan and windows build

* try to fix ci

* ignore ProcessRunner test fail on windows cmake

* enh(File): canExecute throws FileNotFoundException if the file to be executed can't be found in the path.

* Few C++ modernisation changes.

* enh(File): Windows specifics of File::canExecute. Returns false if the file to be executed can't be found using absolutePath.

---------

Co-authored-by: Matej Kenda <matejken@gmail.com>
  • Loading branch information
aleks-f and matejk authored Jul 29, 2024
1 parent f24547c commit 3656f06
Show file tree
Hide file tree
Showing 20 changed files with 361 additions and 37 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,9 @@ jobs:
class CppUnit::TestCaller<class ICMPClientTest>.testBigPing,
class CppUnit::TestCaller<class ICMPSocketTest>.testMTU,
class CppUnit::TestCaller<class HTTPSClientSessionTest>.testProxy,
class CppUnit::TestCaller<class HTTPSStreamFactoryTest>.testProxy
class CppUnit::TestCaller<class HTTPSStreamFactoryTest>.testProxy,
class CppUnit::TestCaller<class FileTest>.testExists,
class CppUnit::TestCaller<class ProcessRunnerTest>.testProcessRunner
steps:
- uses: actions/checkout@v4
- run: cmake -S. -Bcmake-build -DENABLE_NETSSL_WIN=ON -DENABLE_NETSSL=OFF -DENABLE_CRYPTO=OFF -DENABLE_JWT=OFF -DENABLE_DATA=ON -DENABLE_DATA_ODBC=ON -DENABLE_DATA_MYSQL=OFF -DENABLE_DATA_POSTGRESQL=OFF -DENABLE_TESTS=ON
Expand Down
7 changes: 6 additions & 1 deletion Foundation/include/Poco/Any.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,9 @@ class Any
template <typename ValueType>
friend ValueType* AnyCast(Any*);

template <typename ValueType>
friend const ValueType* AnyCast(const Any*);

template <typename ValueType>
friend ValueType* UnsafeAnyCast(Any*);

Expand Down Expand Up @@ -426,7 +429,9 @@ const ValueType* AnyCast(const Any* operand)
/// const MyType* pTmp = AnyCast<MyType>(pAny).
/// Returns nullptr if the types don't match.
{
return AnyCast<ValueType>(const_cast<Any*>(operand));
return operand && operand->type() == typeid(ValueType)
? &static_cast<const Any::Holder<ValueType>*>(operand->content())->_held
: nullptr;
}


Expand Down
1 change: 1 addition & 0 deletions Foundation/include/Poco/Exception.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ POCO_DECLARE_EXCEPTION(Foundation_API, CreateFileException, FileException)
POCO_DECLARE_EXCEPTION(Foundation_API, OpenFileException, FileException)
POCO_DECLARE_EXCEPTION(Foundation_API, WriteFileException, FileException)
POCO_DECLARE_EXCEPTION(Foundation_API, ReadFileException, FileException)
POCO_DECLARE_EXCEPTION(Foundation_API, ExecuteFileException, FileException)
POCO_DECLARE_EXCEPTION(Foundation_API, FileNotReadyException, FileException)
POCO_DECLARE_EXCEPTION(Foundation_API, DirectoryNotEmptyException, FileException)
POCO_DECLARE_EXCEPTION(Foundation_API, UnknownURISchemeException, RuntimeException)
Expand Down
19 changes: 17 additions & 2 deletions Foundation/include/Poco/File.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class Foundation_API File: private FileImpl
/// use the forward slash ("/") as directory separator.
{
public:
typedef FileSizeImpl FileSize;
using FileSize = FileSizeImpl;

enum LinkType
/// Type of link for linkTo().
Expand Down Expand Up @@ -84,7 +84,7 @@ class Foundation_API File: private FileImpl
File(const File& file);
/// Copy constructor.

virtual ~File();
~File() override;
/// Destroys the file.

File& operator = (const File& file);
Expand All @@ -105,9 +105,24 @@ class Foundation_API File: private FileImpl
const std::string& path() const;
/// Returns the path.

std::string absolutePath() const;
/// Returns absolute path.
/// Attempts to find the existing file
/// using curent work directory and the PATH
/// environment variable.
/// If the file doesn't exist, returns empty string.

bool exists() const;
/// Returns true iff the file exists.

bool existsAnywhere() const;
/// If the file path is relative, searches
/// for the file in the current working directory
/// and the environment paths.
/// If the file path is absolute, the
/// functionality is identical to the
/// exists() call.

bool canRead() const;
/// Returns true iff the file is readable.

Expand Down
6 changes: 4 additions & 2 deletions Foundation/include/Poco/File_UNIX.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@


#include "Poco/Foundation.h"
#include "Poco/Timestamp.h"


namespace Poco {
Expand All @@ -32,18 +33,19 @@ class FileImpl
OPT_FAIL_ON_OVERWRITE_IMPL = 0x01
};

typedef UInt64 FileSizeImpl;
using FileSizeImpl = UInt64;

FileImpl();
FileImpl(const std::string& path);
virtual ~FileImpl();
void swapImpl(FileImpl& file);
void setPathImpl(const std::string& path);
const std::string& getPathImpl() const;
std::string getExecutablePathImpl() const;
bool existsImpl() const;
bool canReadImpl() const;
bool canWriteImpl() const;
bool canExecuteImpl() const;
bool canExecuteImpl(const std::string& absolutePath) const;
bool isFileImpl() const;
bool isDirectoryImpl() const;
bool isLinkImpl() const;
Expand Down
7 changes: 4 additions & 3 deletions Foundation/include/Poco/File_VX.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@


#include "Poco/Foundation.h"

#include "Poco/Timestamp.h"

namespace Poco {

Expand All @@ -32,18 +32,19 @@ class FileImpl
OPT_FAIL_ON_OVERWRITE_IMPL = 0x01
};

typedef UInt64 FileSizeImpl;
using FileSizeImpl = UInt64;

FileImpl();
FileImpl(const std::string& path);
virtual ~FileImpl();
void swapImpl(FileImpl& file);
void setPathImpl(const std::string& path);
const std::string& getPathImpl() const;
std::string getExecutablePathImpl() const;
bool existsImpl() const;
bool canReadImpl() const;
bool canWriteImpl() const;
bool canExecuteImpl() const;
bool canExecuteImpl(const std::string& absolutePath) const;
bool isFileImpl() const;
bool isDirectoryImpl() const;
bool isLinkImpl() const;
Expand Down
5 changes: 3 additions & 2 deletions Foundation/include/Poco/File_WIN32U.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,19 @@ class Foundation_API FileImpl
OPT_FAIL_ON_OVERWRITE_IMPL = 0x01
};

typedef UInt64 FileSizeImpl;
using FileSizeImpl = UInt64;

FileImpl();
FileImpl(const std::string& path);
virtual ~FileImpl();
void swapImpl(FileImpl& file);
void setPathImpl(const std::string& path);
const std::string& getPathImpl() const;
std::string getExecutablePathImpl() const;
bool existsImpl() const;
bool canReadImpl() const;
bool canWriteImpl() const;
bool canExecuteImpl() const;
bool canExecuteImpl(const std::string& absolutePath) const;
bool isFileImpl() const;
bool isDirectoryImpl() const;
bool isLinkImpl() const;
Expand Down
2 changes: 1 addition & 1 deletion Foundation/include/Poco/Path.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class Foundation_API Path
PATH_GUESS /// Guess the style by examining the path
};

typedef std::vector<std::string> StringVec;
using StringVec = std::vector<std::string>;

Path();
/// Creates an empty relative path.
Expand Down
34 changes: 33 additions & 1 deletion Foundation/include/Poco/ProcessRunner.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ class Foundation_API ProcessRunner: public Poco::Runnable
int runCount() const;
/// Returns the number of times the process has been executed.

const std::string& error() const;
/// Returns the error message.

private:
static const Poco::ProcessHandle::PID INVALID_PID = -1;
Expand All @@ -141,10 +143,23 @@ class Foundation_API ProcessRunner: public Poco::Runnable
/// Process initialization completion is indicated by new pid in
/// the pid file. If pid file is not specified, there is no waiting.

void checkTimeout(const Poco::Stopwatch& sw, const std::string& msg);
void checkError();
/// If timeout is exceeded, throws TimeoutException with `msg`
/// message.

void checkTimeout(const std::string& msg);
/// If timeout is exceeded, throws TimeoutException with `msg`
/// message.

void checkStatus(const std::string& msg, bool tOut = true);
/// If there were andy errors during process start/stop,
/// throws RuntimeException with the error message;
/// otherwise, if tOut is true and timeout is exceeded, throws
/// TimeoutException with `msg` message.

void setError(const std::string& msg);
/// Sets the error message.

Poco::Thread _t;
std::string _cmd;
Args _args;
Expand All @@ -156,6 +171,9 @@ class Foundation_API ProcessRunner: public Poco::Runnable
std::atomic<bool> _started;
std::atomic<int> _rc;
std::atomic<int> _runCount;
Stopwatch _sw;
std::string _error;
mutable Poco::FastMutex _mutex;
};


Expand Down Expand Up @@ -193,6 +211,20 @@ inline int ProcessRunner::runCount() const
}


inline void ProcessRunner::setError(const std::string& msg)
{
_error = Poco::format("ProcessRunner(%s): %s", cmdLine(), msg);
}


inline const std::string& ProcessRunner::error() const
{
Poco::FastMutex::ScopedLock l(_mutex);

return _error;
}


} // namespace Poco


Expand Down
1 change: 1 addition & 0 deletions Foundation/src/Exception.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ POCO_IMPLEMENT_EXCEPTION(CreateFileException, FileException, "Cannot create file
POCO_IMPLEMENT_EXCEPTION(OpenFileException, FileException, "Cannot open file")
POCO_IMPLEMENT_EXCEPTION(WriteFileException, FileException, "Cannot write file")
POCO_IMPLEMENT_EXCEPTION(ReadFileException, FileException, "Cannot read file")
POCO_IMPLEMENT_EXCEPTION(ExecuteFileException, FileException, "Cannot execute file")
POCO_IMPLEMENT_EXCEPTION(FileNotReadyException, FileException, "File not ready")
POCO_IMPLEMENT_EXCEPTION(DirectoryNotEmptyException, FileException, "Directory not empty")
POCO_IMPLEMENT_EXCEPTION(UnknownURISchemeException, RuntimeException, "Unknown URI scheme")
Expand Down
76 changes: 75 additions & 1 deletion Foundation/src/File.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "Poco/File.h"
#include "Poco/Path.h"
#include "Poco/DirectoryIterator.h"
#include "Poco/Environment.h"
#include "Poco/StringTokenizer.h"


#if defined(POCO_OS_FAMILY_WINDOWS)
Expand Down Expand Up @@ -95,12 +97,77 @@ void File::swap(File& file) noexcept
}


std::string File::absolutePath() const
{
std::string ret;

if (Path(path()).isAbsolute())
// TODO: Should this return empty string if file does not exists to be consistent
// with the function documentation?
ret = getPathImpl();
else
{
Path curPath(Path::current());
curPath.append(path());
if (File(curPath).exists())
ret = curPath.toString();
else
{
const std::string envPath = Environment::get("PATH", "");
const std::string pathSeparator(1, Path::pathSeparator());
if (!envPath.empty())
{
const StringTokenizer st(envPath, pathSeparator,
StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);

for (const auto& p: st)
{
try
{
std::string fileName(p);
if (p.size() && p.back() != Path::separator())
fileName.append(1, Path::separator());
fileName.append(path());
if (File(fileName).exists())
{
ret = fileName;
break;
}
}
catch (const Poco::PathSyntaxException&)
{
// shield against bad PATH environment entries
}
}
}
}
}

return ret;
}


bool File::exists() const
{
if (path().empty()) return false;
return existsImpl();
}


bool File::existsAnywhere() const
{
if (path().empty()) return false;

if (Path(path()).isAbsolute())
return existsImpl();

if (File(absolutePath()).exists())
return true;

return false;
}


bool File::canRead() const
{
return canReadImpl();
Expand All @@ -115,7 +182,14 @@ bool File::canWrite() const

bool File::canExecute() const
{
return canExecuteImpl();
// Resolve (platform-specific) executable path and absolute path from relative.
const auto execPath { getExecutablePathImpl() };
const auto absPath { File(execPath).absolutePath() };
if (absPath.empty() || !File(absPath).exists())
{
return false;
}
return canExecuteImpl(absPath);
}


Expand Down
13 changes: 10 additions & 3 deletions Foundation/src/File_UNIX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "Poco/Buffer.h"
#include "Poco/Exception.h"
#include "Poco/Error.h"
#include "Poco/Path.h"
#include <algorithm>
#include <sys/stat.h>
#include <sys/types.h>
Expand Down Expand Up @@ -80,6 +81,12 @@ void FileImpl::setPathImpl(const std::string& path)
}


std::string FileImpl::getExecutablePathImpl() const
{
return _path;
}


bool FileImpl::existsImpl() const
{
poco_assert (!_path.empty());
Expand Down Expand Up @@ -127,12 +134,12 @@ bool FileImpl::canWriteImpl() const
}


bool FileImpl::canExecuteImpl() const
bool FileImpl::canExecuteImpl(const std::string& absolutePath) const
{
poco_assert (!_path.empty());
poco_assert (!absolutePath.empty());

struct stat st;
if (stat(_path.c_str(), &st) == 0)
if (stat(absolutePath.c_str(), &st) == 0)
{
if (st.st_uid == geteuid() || geteuid() == 0)
return (st.st_mode & S_IXUSR) != 0;
Expand Down
Loading

0 comments on commit 3656f06

Please sign in to comment.