mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-26 18:42:41 +01:00
* 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>
This commit is contained in:
committed by
GitHub
parent
f24547cdcf
commit
3656f069e1
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -555,7 +555,9 @@ jobs:
|
|||||||
class CppUnit::TestCaller<class ICMPClientTest>.testBigPing,
|
class CppUnit::TestCaller<class ICMPClientTest>.testBigPing,
|
||||||
class CppUnit::TestCaller<class ICMPSocketTest>.testMTU,
|
class CppUnit::TestCaller<class ICMPSocketTest>.testMTU,
|
||||||
class CppUnit::TestCaller<class HTTPSClientSessionTest>.testProxy,
|
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:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- 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
|
- 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
|
||||||
|
|||||||
@@ -388,6 +388,9 @@ private:
|
|||||||
template <typename ValueType>
|
template <typename ValueType>
|
||||||
friend ValueType* AnyCast(Any*);
|
friend ValueType* AnyCast(Any*);
|
||||||
|
|
||||||
|
template <typename ValueType>
|
||||||
|
friend const ValueType* AnyCast(const Any*);
|
||||||
|
|
||||||
template <typename ValueType>
|
template <typename ValueType>
|
||||||
friend ValueType* UnsafeAnyCast(Any*);
|
friend ValueType* UnsafeAnyCast(Any*);
|
||||||
|
|
||||||
@@ -426,7 +429,9 @@ const ValueType* AnyCast(const Any* operand)
|
|||||||
/// const MyType* pTmp = AnyCast<MyType>(pAny).
|
/// const MyType* pTmp = AnyCast<MyType>(pAny).
|
||||||
/// Returns nullptr if the types don't match.
|
/// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -246,6 +246,7 @@ POCO_DECLARE_EXCEPTION(Foundation_API, CreateFileException, FileException)
|
|||||||
POCO_DECLARE_EXCEPTION(Foundation_API, OpenFileException, FileException)
|
POCO_DECLARE_EXCEPTION(Foundation_API, OpenFileException, FileException)
|
||||||
POCO_DECLARE_EXCEPTION(Foundation_API, WriteFileException, FileException)
|
POCO_DECLARE_EXCEPTION(Foundation_API, WriteFileException, FileException)
|
||||||
POCO_DECLARE_EXCEPTION(Foundation_API, ReadFileException, 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, FileNotReadyException, FileException)
|
||||||
POCO_DECLARE_EXCEPTION(Foundation_API, DirectoryNotEmptyException, FileException)
|
POCO_DECLARE_EXCEPTION(Foundation_API, DirectoryNotEmptyException, FileException)
|
||||||
POCO_DECLARE_EXCEPTION(Foundation_API, UnknownURISchemeException, RuntimeException)
|
POCO_DECLARE_EXCEPTION(Foundation_API, UnknownURISchemeException, RuntimeException)
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class Foundation_API File: private FileImpl
|
|||||||
/// use the forward slash ("/") as directory separator.
|
/// use the forward slash ("/") as directory separator.
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef FileSizeImpl FileSize;
|
using FileSize = FileSizeImpl;
|
||||||
|
|
||||||
enum LinkType
|
enum LinkType
|
||||||
/// Type of link for linkTo().
|
/// Type of link for linkTo().
|
||||||
@@ -84,7 +84,7 @@ public:
|
|||||||
File(const File& file);
|
File(const File& file);
|
||||||
/// Copy constructor.
|
/// Copy constructor.
|
||||||
|
|
||||||
virtual ~File();
|
~File() override;
|
||||||
/// Destroys the file.
|
/// Destroys the file.
|
||||||
|
|
||||||
File& operator = (const File& file);
|
File& operator = (const File& file);
|
||||||
@@ -105,9 +105,24 @@ public:
|
|||||||
const std::string& path() const;
|
const std::string& path() const;
|
||||||
/// Returns the path.
|
/// 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;
|
bool exists() const;
|
||||||
/// Returns true iff the file exists.
|
/// 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;
|
bool canRead() const;
|
||||||
/// Returns true iff the file is readable.
|
/// Returns true iff the file is readable.
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "Poco/Foundation.h"
|
#include "Poco/Foundation.h"
|
||||||
|
#include "Poco/Timestamp.h"
|
||||||
|
|
||||||
|
|
||||||
namespace Poco {
|
namespace Poco {
|
||||||
@@ -32,7 +33,7 @@ protected:
|
|||||||
OPT_FAIL_ON_OVERWRITE_IMPL = 0x01
|
OPT_FAIL_ON_OVERWRITE_IMPL = 0x01
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef UInt64 FileSizeImpl;
|
using FileSizeImpl = UInt64;
|
||||||
|
|
||||||
FileImpl();
|
FileImpl();
|
||||||
FileImpl(const std::string& path);
|
FileImpl(const std::string& path);
|
||||||
@@ -40,10 +41,11 @@ protected:
|
|||||||
void swapImpl(FileImpl& file);
|
void swapImpl(FileImpl& file);
|
||||||
void setPathImpl(const std::string& path);
|
void setPathImpl(const std::string& path);
|
||||||
const std::string& getPathImpl() const;
|
const std::string& getPathImpl() const;
|
||||||
|
std::string getExecutablePathImpl() const;
|
||||||
bool existsImpl() const;
|
bool existsImpl() const;
|
||||||
bool canReadImpl() const;
|
bool canReadImpl() const;
|
||||||
bool canWriteImpl() const;
|
bool canWriteImpl() const;
|
||||||
bool canExecuteImpl() const;
|
bool canExecuteImpl(const std::string& absolutePath) const;
|
||||||
bool isFileImpl() const;
|
bool isFileImpl() const;
|
||||||
bool isDirectoryImpl() const;
|
bool isDirectoryImpl() const;
|
||||||
bool isLinkImpl() const;
|
bool isLinkImpl() const;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "Poco/Foundation.h"
|
#include "Poco/Foundation.h"
|
||||||
|
#include "Poco/Timestamp.h"
|
||||||
|
|
||||||
namespace Poco {
|
namespace Poco {
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ protected:
|
|||||||
OPT_FAIL_ON_OVERWRITE_IMPL = 0x01
|
OPT_FAIL_ON_OVERWRITE_IMPL = 0x01
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef UInt64 FileSizeImpl;
|
using FileSizeImpl = UInt64;
|
||||||
|
|
||||||
FileImpl();
|
FileImpl();
|
||||||
FileImpl(const std::string& path);
|
FileImpl(const std::string& path);
|
||||||
@@ -40,10 +40,11 @@ protected:
|
|||||||
void swapImpl(FileImpl& file);
|
void swapImpl(FileImpl& file);
|
||||||
void setPathImpl(const std::string& path);
|
void setPathImpl(const std::string& path);
|
||||||
const std::string& getPathImpl() const;
|
const std::string& getPathImpl() const;
|
||||||
|
std::string getExecutablePathImpl() const;
|
||||||
bool existsImpl() const;
|
bool existsImpl() const;
|
||||||
bool canReadImpl() const;
|
bool canReadImpl() const;
|
||||||
bool canWriteImpl() const;
|
bool canWriteImpl() const;
|
||||||
bool canExecuteImpl() const;
|
bool canExecuteImpl(const std::string& absolutePath) const;
|
||||||
bool isFileImpl() const;
|
bool isFileImpl() const;
|
||||||
bool isDirectoryImpl() const;
|
bool isDirectoryImpl() const;
|
||||||
bool isLinkImpl() const;
|
bool isLinkImpl() const;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ protected:
|
|||||||
OPT_FAIL_ON_OVERWRITE_IMPL = 0x01
|
OPT_FAIL_ON_OVERWRITE_IMPL = 0x01
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef UInt64 FileSizeImpl;
|
using FileSizeImpl = UInt64;
|
||||||
|
|
||||||
FileImpl();
|
FileImpl();
|
||||||
FileImpl(const std::string& path);
|
FileImpl(const std::string& path);
|
||||||
@@ -41,10 +41,11 @@ protected:
|
|||||||
void swapImpl(FileImpl& file);
|
void swapImpl(FileImpl& file);
|
||||||
void setPathImpl(const std::string& path);
|
void setPathImpl(const std::string& path);
|
||||||
const std::string& getPathImpl() const;
|
const std::string& getPathImpl() const;
|
||||||
|
std::string getExecutablePathImpl() const;
|
||||||
bool existsImpl() const;
|
bool existsImpl() const;
|
||||||
bool canReadImpl() const;
|
bool canReadImpl() const;
|
||||||
bool canWriteImpl() const;
|
bool canWriteImpl() const;
|
||||||
bool canExecuteImpl() const;
|
bool canExecuteImpl(const std::string& absolutePath) const;
|
||||||
bool isFileImpl() const;
|
bool isFileImpl() const;
|
||||||
bool isDirectoryImpl() const;
|
bool isDirectoryImpl() const;
|
||||||
bool isLinkImpl() const;
|
bool isLinkImpl() const;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public:
|
|||||||
PATH_GUESS /// Guess the style by examining the path
|
PATH_GUESS /// Guess the style by examining the path
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<std::string> StringVec;
|
using StringVec = std::vector<std::string>;
|
||||||
|
|
||||||
Path();
|
Path();
|
||||||
/// Creates an empty relative path.
|
/// Creates an empty relative path.
|
||||||
|
|||||||
@@ -121,6 +121,8 @@ public:
|
|||||||
int runCount() const;
|
int runCount() const;
|
||||||
/// Returns the number of times the process has been executed.
|
/// Returns the number of times the process has been executed.
|
||||||
|
|
||||||
|
const std::string& error() const;
|
||||||
|
/// Returns the error message.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const Poco::ProcessHandle::PID INVALID_PID = -1;
|
static const Poco::ProcessHandle::PID INVALID_PID = -1;
|
||||||
@@ -141,10 +143,23 @@ private:
|
|||||||
/// Process initialization completion is indicated by new pid in
|
/// Process initialization completion is indicated by new pid in
|
||||||
/// the pid file. If pid file is not specified, there is no waiting.
|
/// 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`
|
/// If timeout is exceeded, throws TimeoutException with `msg`
|
||||||
/// message.
|
/// 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;
|
Poco::Thread _t;
|
||||||
std::string _cmd;
|
std::string _cmd;
|
||||||
Args _args;
|
Args _args;
|
||||||
@@ -156,6 +171,9 @@ private:
|
|||||||
std::atomic<bool> _started;
|
std::atomic<bool> _started;
|
||||||
std::atomic<int> _rc;
|
std::atomic<int> _rc;
|
||||||
std::atomic<int> _runCount;
|
std::atomic<int> _runCount;
|
||||||
|
Stopwatch _sw;
|
||||||
|
std::string _error;
|
||||||
|
mutable Poco::FastMutex _mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -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
|
} // namespace Poco
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ POCO_IMPLEMENT_EXCEPTION(CreateFileException, FileException, "Cannot create file
|
|||||||
POCO_IMPLEMENT_EXCEPTION(OpenFileException, FileException, "Cannot open file")
|
POCO_IMPLEMENT_EXCEPTION(OpenFileException, FileException, "Cannot open file")
|
||||||
POCO_IMPLEMENT_EXCEPTION(WriteFileException, FileException, "Cannot write file")
|
POCO_IMPLEMENT_EXCEPTION(WriteFileException, FileException, "Cannot write file")
|
||||||
POCO_IMPLEMENT_EXCEPTION(ReadFileException, FileException, "Cannot read 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(FileNotReadyException, FileException, "File not ready")
|
||||||
POCO_IMPLEMENT_EXCEPTION(DirectoryNotEmptyException, FileException, "Directory not empty")
|
POCO_IMPLEMENT_EXCEPTION(DirectoryNotEmptyException, FileException, "Directory not empty")
|
||||||
POCO_IMPLEMENT_EXCEPTION(UnknownURISchemeException, RuntimeException, "Unknown URI scheme")
|
POCO_IMPLEMENT_EXCEPTION(UnknownURISchemeException, RuntimeException, "Unknown URI scheme")
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
#include "Poco/File.h"
|
#include "Poco/File.h"
|
||||||
#include "Poco/Path.h"
|
#include "Poco/Path.h"
|
||||||
#include "Poco/DirectoryIterator.h"
|
#include "Poco/DirectoryIterator.h"
|
||||||
|
#include "Poco/Environment.h"
|
||||||
|
#include "Poco/StringTokenizer.h"
|
||||||
|
|
||||||
|
|
||||||
#if defined(POCO_OS_FAMILY_WINDOWS)
|
#if defined(POCO_OS_FAMILY_WINDOWS)
|
||||||
@@ -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
|
bool File::exists() const
|
||||||
{
|
{
|
||||||
|
if (path().empty()) return false;
|
||||||
return existsImpl();
|
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
|
bool File::canRead() const
|
||||||
{
|
{
|
||||||
return canReadImpl();
|
return canReadImpl();
|
||||||
@@ -115,7 +182,14 @@ bool File::canWrite() const
|
|||||||
|
|
||||||
bool File::canExecute() 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "Poco/Buffer.h"
|
#include "Poco/Buffer.h"
|
||||||
#include "Poco/Exception.h"
|
#include "Poco/Exception.h"
|
||||||
#include "Poco/Error.h"
|
#include "Poco/Error.h"
|
||||||
|
#include "Poco/Path.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@@ -80,6 +81,12 @@ void FileImpl::setPathImpl(const std::string& path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string FileImpl::getExecutablePathImpl() const
|
||||||
|
{
|
||||||
|
return _path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool FileImpl::existsImpl() const
|
bool FileImpl::existsImpl() const
|
||||||
{
|
{
|
||||||
poco_assert (!_path.empty());
|
poco_assert (!_path.empty());
|
||||||
@@ -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;
|
struct stat st;
|
||||||
if (stat(_path.c_str(), &st) == 0)
|
if (stat(absolutePath.c_str(), &st) == 0)
|
||||||
{
|
{
|
||||||
if (st.st_uid == geteuid() || geteuid() == 0)
|
if (st.st_uid == geteuid() || geteuid() == 0)
|
||||||
return (st.st_mode & S_IXUSR) != 0;
|
return (st.st_mode & S_IXUSR) != 0;
|
||||||
|
|||||||
@@ -62,6 +62,12 @@ void FileImpl::setPathImpl(const std::string& path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string FileImpl::getExecutablePathImpl() const
|
||||||
|
{
|
||||||
|
return _path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool FileImpl::existsImpl() const
|
bool FileImpl::existsImpl() const
|
||||||
{
|
{
|
||||||
poco_assert (!_path.empty());
|
poco_assert (!_path.empty());
|
||||||
@@ -87,7 +93,7 @@ bool FileImpl::canWriteImpl() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool FileImpl::canExecuteImpl() const
|
bool FileImpl::canExecuteImpl(const std::string& absolutePath) const
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "Poco/File_WIN32U.h"
|
#include "Poco/File_WIN32U.h"
|
||||||
#include "Poco/Exception.h"
|
#include "Poco/Exception.h"
|
||||||
|
#include "Poco/Path.h"
|
||||||
#include "Poco/String.h"
|
#include "Poco/String.h"
|
||||||
#include "Poco/UnicodeConverter.h"
|
#include "Poco/UnicodeConverter.h"
|
||||||
#include "Poco/UnWindows.h"
|
#include "Poco/UnWindows.h"
|
||||||
@@ -88,6 +89,19 @@ void FileImpl::setPathImpl(const std::string& path)
|
|||||||
convertPath(_path, _upath);
|
convertPath(_path, _upath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string FileImpl::getExecutablePathImpl() const
|
||||||
|
{
|
||||||
|
// Windows specific: An executable can be invoked without
|
||||||
|
// the extension .exe, but the file has it nevertheless.
|
||||||
|
// This function appends extension "exe" if the file path does not have it.
|
||||||
|
Path p(_path);
|
||||||
|
if (!p.getExtension().empty())
|
||||||
|
{
|
||||||
|
return _path;
|
||||||
|
}
|
||||||
|
return p.setExtension("exe"s).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool FileImpl::existsImpl() const
|
bool FileImpl::existsImpl() const
|
||||||
{
|
{
|
||||||
@@ -140,9 +154,9 @@ bool FileImpl::canWriteImpl() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool FileImpl::canExecuteImpl() const
|
bool FileImpl::canExecuteImpl(const std::string& absolutePath) const
|
||||||
{
|
{
|
||||||
Path p(_path);
|
Path p(absolutePath);
|
||||||
return icompare(p.getExtension(), "exe") == 0;
|
return icompare(p.getExtension(), "exe") == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "Poco/File.h"
|
#include "Poco/File.h"
|
||||||
#include "Poco/Path.h"
|
#include "Poco/Path.h"
|
||||||
#include "Poco/String.h"
|
#include "Poco/String.h"
|
||||||
|
#include "Poco/Error.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
@@ -104,15 +105,52 @@ std::string ProcessRunner::cmdLine() const
|
|||||||
|
|
||||||
void ProcessRunner::run()
|
void ProcessRunner::run()
|
||||||
{
|
{
|
||||||
|
int errHandle = 0;
|
||||||
|
int errPID = 0;
|
||||||
|
int errRC = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
Poco::FastMutex::ScopedLock l(_mutex);
|
||||||
|
_error.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
_pid = INVALID_PID;
|
||||||
|
_pPH = nullptr;
|
||||||
|
|
||||||
ProcessHandle* pPH = nullptr;
|
ProcessHandle* pPH = nullptr;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_pPH = pPH = new ProcessHandle(Process::launch(_cmd, _args, _options));
|
_pPH = pPH = new ProcessHandle(Process::launch(_cmd, _args, _options));
|
||||||
|
errHandle = Error::last();
|
||||||
|
|
||||||
_pid = pPH->id();
|
_pid = pPH->id();
|
||||||
|
errPID = Error::last();
|
||||||
|
|
||||||
_rc = pPH->wait();
|
_rc = pPH->wait();
|
||||||
|
errRC = Error::last();
|
||||||
|
|
||||||
|
if (errHandle || errPID || errRC || _rc != 0)
|
||||||
|
{
|
||||||
|
Poco::FastMutex::ScopedLock l(_mutex);
|
||||||
|
|
||||||
|
Poco::format(_error, "ProcessRunner::run() error; "
|
||||||
|
"handle=%d (%d:%s); pid=%d (%d:%s); return=%d (%d:%s)",
|
||||||
|
(pPH ? pPH->id() : 0), errHandle, Error::getMessage(errHandle),
|
||||||
|
_pid.load(), errPID, Error::getMessage(errPID),
|
||||||
|
_rc.load(), errRC, Error::getMessage(errRC));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Poco::Exception& ex)
|
||||||
|
{
|
||||||
|
setError(ex.displayText());
|
||||||
|
}
|
||||||
|
catch (std::exception& ex)
|
||||||
|
{
|
||||||
|
setError(ex.what());
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
|
setError("Unknown exception"s);
|
||||||
}
|
}
|
||||||
|
|
||||||
_pid = INVALID_PID;
|
_pid = INVALID_PID;
|
||||||
@@ -127,7 +165,7 @@ void ProcessRunner::stop()
|
|||||||
if (_started)
|
if (_started)
|
||||||
{
|
{
|
||||||
PID pid;
|
PID pid;
|
||||||
Stopwatch sw; sw.start();
|
_sw.restart();
|
||||||
if (_pPH.exchange(nullptr) && ((pid = _pid.exchange(INVALID_PID))) != INVALID_PID)
|
if (_pPH.exchange(nullptr) && ((pid = _pid.exchange(INVALID_PID))) != INVALID_PID)
|
||||||
{
|
{
|
||||||
while (Process::isRunning(pid))
|
while (Process::isRunning(pid))
|
||||||
@@ -135,9 +173,9 @@ void ProcessRunner::stop()
|
|||||||
if (pid > 0)
|
if (pid > 0)
|
||||||
{
|
{
|
||||||
Process::requestTermination(pid);
|
Process::requestTermination(pid);
|
||||||
checkTimeout(sw, "Waiting for process termination");
|
checkStatus("Waiting for process termination");
|
||||||
}
|
}
|
||||||
else throw Poco::IllegalStateException("Invalid PID, can;t terminate process");
|
else throw Poco::IllegalStateException("Invalid PID, can't terminate process");
|
||||||
}
|
}
|
||||||
_t.join();
|
_t.join();
|
||||||
}
|
}
|
||||||
@@ -150,9 +188,8 @@ void ProcessRunner::stop()
|
|||||||
_pidFile.clear();
|
_pidFile.clear();
|
||||||
std::string msg;
|
std::string msg;
|
||||||
Poco::format(msg, "Waiting for PID file (pidFile: '%s')", _pidFile);
|
Poco::format(msg, "Waiting for PID file (pidFile: '%s')", _pidFile);
|
||||||
sw.restart();
|
_sw.restart();
|
||||||
while (pidFile.exists())
|
while (pidFile.exists()) checkStatus(msg);
|
||||||
checkTimeout(sw, msg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,9 +197,18 @@ void ProcessRunner::stop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ProcessRunner::checkTimeout(const Stopwatch& sw, const std::string& msg)
|
void ProcessRunner::checkError()
|
||||||
{
|
{
|
||||||
if (sw.elapsedSeconds() > _timeout)
|
Poco::FastMutex::ScopedLock l(_mutex);
|
||||||
|
|
||||||
|
if (!_error.empty())
|
||||||
|
throw Poco::RuntimeException(_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ProcessRunner::checkTimeout(const std::string& msg)
|
||||||
|
{
|
||||||
|
if (_sw.elapsedSeconds() > _timeout)
|
||||||
{
|
{
|
||||||
throw Poco::TimeoutException(
|
throw Poco::TimeoutException(
|
||||||
Poco::format("ProcessRunner::checkTimeout(): %s", msg));
|
Poco::format("ProcessRunner::checkTimeout(): %s", msg));
|
||||||
@@ -171,31 +217,50 @@ void ProcessRunner::checkTimeout(const Stopwatch& sw, const std::string& msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ProcessRunner::checkStatus(const std::string& msg, bool tOut)
|
||||||
|
{
|
||||||
|
checkError();
|
||||||
|
if (tOut) checkTimeout(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ProcessRunner::start()
|
void ProcessRunner::start()
|
||||||
{
|
{
|
||||||
if (!_started.exchange(true))
|
if (!_started.exchange(true))
|
||||||
{
|
{
|
||||||
|
File exe(_cmd);
|
||||||
|
if (!exe.existsAnywhere())
|
||||||
|
{
|
||||||
|
throw Poco::FileNotFoundException(
|
||||||
|
Poco::format("ProcessRunner::start(%s): command not found", _cmd));
|
||||||
|
}
|
||||||
|
else if (!File(exe.absolutePath()).canExecute())
|
||||||
|
{
|
||||||
|
throw Poco::ExecuteFileException(
|
||||||
|
Poco::format("ProcessRunner::start(%s): cannot execute", _cmd));
|
||||||
|
}
|
||||||
|
|
||||||
int prevRunCnt = runCount();
|
int prevRunCnt = runCount();
|
||||||
|
|
||||||
_t.start(*this);
|
_t.start(*this);
|
||||||
|
|
||||||
std::string msg;
|
std::string msg;
|
||||||
Poco::format(msg, "Waiting for process to start (pidFile: '%s')", _pidFile);
|
Poco::format(msg, "Waiting for process to start (pidFile: '%s')", _pidFile);
|
||||||
Stopwatch sw; sw.start();
|
_sw.restart();
|
||||||
|
|
||||||
// wait for the process to be either running or completed by monitoring run counts.
|
// wait for the process to be either running or completed by monitoring run counts.
|
||||||
while (!running() && prevRunCnt >= runCount()) checkTimeout(sw, msg);
|
while (!running() && prevRunCnt >= runCount()) checkStatus(msg);
|
||||||
|
|
||||||
// we could wait for the process handle != INVALID_PID,
|
// we could wait for the process handle != INVALID_PID,
|
||||||
// but if pidFile name was given, we should wait for
|
// but if pidFile name was given, we should wait for
|
||||||
// the process to write it
|
// the process to write it
|
||||||
if (!_pidFile.empty())
|
if (!_pidFile.empty())
|
||||||
{
|
{
|
||||||
sw.restart();
|
_sw.restart();
|
||||||
// wait until process is fully initialized
|
// wait until process is fully initialized
|
||||||
File pidFile(_pidFile);
|
File pidFile(_pidFile);
|
||||||
while (!pidFile.exists())
|
while (!pidFile.exists())
|
||||||
checkTimeout(sw, "waiting for PID file");
|
checkStatus(Poco::format("waiting for PID file '%s' creation.", _pidFile));
|
||||||
|
|
||||||
// verify that the file content is actually the process PID
|
// verify that the file content is actually the process PID
|
||||||
FileInputStream fis(_pidFile);
|
FileInputStream fis(_pidFile);
|
||||||
@@ -205,7 +270,7 @@ void ProcessRunner::start()
|
|||||||
while (fPID != pid())
|
while (fPID != pid())
|
||||||
{
|
{
|
||||||
fis.clear(); fis.seekg(0); fis >> fPID;
|
fis.clear(); fis.seekg(0); fis >> fPID;
|
||||||
checkTimeout(sw, Poco::format("waiting for new PID (%s)", _pidFile));
|
checkStatus(Poco::format("waiting for new PID (%s)", _pidFile));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ void AnyTest::testAnyCastToReference()
|
|||||||
|
|
||||||
int& ra = AnyCast<int &>(a);
|
int& ra = AnyCast<int &>(a);
|
||||||
int const& ra_c = AnyCast<int const &>(a);
|
int const& ra_c = AnyCast<int const &>(a);
|
||||||
// NOTE: The following two AnyCasts will trigger the
|
// NOTE: The volatile AnyCasts will trigger the
|
||||||
// undefined behavior sanitizer.
|
// undefined behavior sanitizer.
|
||||||
int volatile& ra_v = AnyCast<int volatile &>(a);
|
int volatile& ra_v = AnyCast<int volatile &>(a);
|
||||||
int const volatile& ra_cv = AnyCast<int const volatile&>(a);
|
int const volatile& ra_cv = AnyCast<int const volatile&>(a);
|
||||||
|
|||||||
@@ -18,7 +18,9 @@
|
|||||||
#include "Poco/Thread.h"
|
#include "Poco/Thread.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#if defined(POCO_OS_FAMILY_WINDOWS)
|
||||||
|
#include <Windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using Poco::File;
|
using Poco::File;
|
||||||
using Poco::TemporaryFile;
|
using Poco::TemporaryFile;
|
||||||
@@ -192,6 +194,61 @@ void FileTest::testCreateFile()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FileTest::testExists()
|
||||||
|
{
|
||||||
|
assertFalse (File("").exists());
|
||||||
|
{
|
||||||
|
File f("testfile.dat");
|
||||||
|
f.createFile();
|
||||||
|
assertTrue (f.exists());
|
||||||
|
assertTrue (f.existsAnywhere());
|
||||||
|
assertFalse (f.canExecute());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
File f("/testfile.dat");
|
||||||
|
assertFalse (f.exists());
|
||||||
|
assertFalse (f.existsAnywhere());
|
||||||
|
assertFalse (f.canExecute());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
#if defined(POCO_OS_FAMILY_UNIX)
|
||||||
|
File f("echo");
|
||||||
|
File f2("/dev/null");
|
||||||
|
#elif defined(POCO_OS_FAMILY_WINDOWS)
|
||||||
|
std::string buffer(MAX_PATH, 0);
|
||||||
|
UINT r = GetSystemDirectoryA(buffer.data(), static_cast<UINT>(buffer.size()));
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
Path p(buffer);
|
||||||
|
p.makeDirectory().makeAbsolute().makeParent();
|
||||||
|
buffer = p.toString();
|
||||||
|
buffer.append("win.ini");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer = R"(c:\windows\win.ini)";
|
||||||
|
}
|
||||||
|
File f("cmd.exe");
|
||||||
|
File f2(buffer);
|
||||||
|
|
||||||
|
File f3("cmd");
|
||||||
|
assertTrue (f3.canExecute());
|
||||||
|
File f4("cmd-nonexistent");
|
||||||
|
assertFalse (f4.canExecute());
|
||||||
|
#endif
|
||||||
|
assertFalse (f.exists());
|
||||||
|
assertTrue (f.existsAnywhere());
|
||||||
|
assertTrue (f.canExecute());
|
||||||
|
|
||||||
|
assertTrue (f2.exists());
|
||||||
|
assertTrue (f2.existsAnywhere());
|
||||||
|
assertFalse (f2.canExecute());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void FileTest::testFileAttributes2()
|
void FileTest::testFileAttributes2()
|
||||||
{
|
{
|
||||||
TemporaryFile f;
|
TemporaryFile f;
|
||||||
@@ -660,6 +717,7 @@ CppUnit::Test* FileTest::suite()
|
|||||||
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("FileTest");
|
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("FileTest");
|
||||||
|
|
||||||
CppUnit_addTest(pSuite, FileTest, testCreateFile);
|
CppUnit_addTest(pSuite, FileTest, testCreateFile);
|
||||||
|
CppUnit_addTest(pSuite, FileTest, testExists);
|
||||||
CppUnit_addTest(pSuite, FileTest, testFileAttributes1);
|
CppUnit_addTest(pSuite, FileTest, testFileAttributes1);
|
||||||
CppUnit_addTest(pSuite, FileTest, testFileAttributes2);
|
CppUnit_addTest(pSuite, FileTest, testFileAttributes2);
|
||||||
CppUnit_addTest(pSuite, FileTest, testFileAttributes3);
|
CppUnit_addTest(pSuite, FileTest, testFileAttributes3);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public:
|
|||||||
|
|
||||||
void testFileAttributes1();
|
void testFileAttributes1();
|
||||||
void testCreateFile();
|
void testCreateFile();
|
||||||
|
void testExists();
|
||||||
void testFileAttributes2();
|
void testFileAttributes2();
|
||||||
void testFileAttributes3();
|
void testFileAttributes3();
|
||||||
void testCompare();
|
void testCompare();
|
||||||
|
|||||||
@@ -349,6 +349,9 @@ void PathTest::testParseUnix4()
|
|||||||
assertTrue (!p.isDirectory());
|
assertTrue (!p.isDirectory());
|
||||||
assertTrue (p.isFile());
|
assertTrue (p.isFile());
|
||||||
assertTrue (p.toString(Path::PATH_UNIX) == "a/b/c/d");
|
assertTrue (p.toString(Path::PATH_UNIX) == "a/b/c/d");
|
||||||
|
|
||||||
|
p.makeDirectory();
|
||||||
|
assertTrue (p.toString(Path::PATH_UNIX) == "a/b/c/d/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -111,11 +111,11 @@ void ProcessRunnerTest::testProcessRunner()
|
|||||||
assertTrue (pr.cmdLine() == cmdLine(cmd, args));
|
assertTrue (pr.cmdLine() == cmdLine(cmd, args));
|
||||||
assertFalse (pr.running());
|
assertFalse (pr.running());
|
||||||
pr.start();
|
pr.start();
|
||||||
|
|
||||||
Stopwatch sw; sw.start();
|
Stopwatch sw; sw.start();
|
||||||
while (!pr.running())
|
while (!pr.running())
|
||||||
checkTimeout(sw, "Waiting for process to start", 1000, __LINE__);
|
checkTimeout(sw, "Waiting for process to start", 1000, __LINE__);
|
||||||
|
|
||||||
assertTrue (pr.running());
|
assertTrue (pr.running());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -264,6 +264,42 @@ void ProcessRunnerTest::testProcessRunner()
|
|||||||
}
|
}
|
||||||
assertTrue (!File(pidFile).exists());
|
assertTrue (!File(pidFile).exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// non-existent executable with no PID file created
|
||||||
|
{
|
||||||
|
std::string cmd = "nonexistent_123-xyz";
|
||||||
|
std::vector<std::string> args;
|
||||||
|
char c = Path::separator();
|
||||||
|
std::string pidFile = Poco::format("run%c%s.pid", c, name);
|
||||||
|
{
|
||||||
|
std::unique_ptr<ProcessRunner> pr;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pr.reset(new ProcessRunner(cmd, args));
|
||||||
|
failmsg("ProcessRunner should throw an exception.");
|
||||||
|
} catch(const Poco::FileException& e) {}
|
||||||
|
}
|
||||||
|
assertTrue (!File(pidFile).exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
// non-existent executable with PID file created
|
||||||
|
{
|
||||||
|
std::string cmd = "nonexistent_123-xyz";
|
||||||
|
std::vector<std::string> args;
|
||||||
|
char c = Path::separator();
|
||||||
|
std::string pidFile = Poco::format("run%c%s.pid", c, name);
|
||||||
|
args.push_back(std::string("-p=").append(pidFile));
|
||||||
|
{
|
||||||
|
std::unique_ptr<ProcessRunner> pr;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pr.reset(new ProcessRunner(cmd, args));
|
||||||
|
failmsg("ProcessRunner should throw an exception.");
|
||||||
|
} catch(const Poco::FileException& e) {}
|
||||||
|
}
|
||||||
|
assertTrue (!File(pidFile).exists());
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(POCO_OS_FAMILY_UNIX)
|
#if defined(POCO_OS_FAMILY_UNIX)
|
||||||
// start process launching multiple threads
|
// start process launching multiple threads
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user