mirror of
				https://github.com/pocoproject/poco.git
				synced 2025-10-27 11:06:50 +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:
		 Aleksandar Fabijanic
					Aleksandar Fabijanic
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						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/"); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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