diff options
Diffstat (limited to 'Platform/Unix/Process.cpp')
| -rw-r--r-- | Platform/Unix/Process.cpp | 202 | 
1 files changed, 202 insertions, 0 deletions
| diff --git a/Platform/Unix/Process.cpp b/Platform/Unix/Process.cpp new file mode 100644 index 0000000..d99dff8 --- /dev/null +++ b/Platform/Unix/Process.cpp @@ -0,0 +1,202 @@ +/* + Copyright (c) 2008 TrueCrypt Developers Association. All rights reserved. + + Governed by the TrueCrypt License 3.0 the full text of which is contained in + the file License.txt included in TrueCrypt binary and source code distribution + packages. +*/ + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include "Process.h" +#include "Platform/Exception.h" +#include "Platform/FileStream.h" +#include "Platform/ForEach.h" +#include "Platform/MemoryStream.h" +#include "Platform/SystemException.h" +#include "Platform/StringConverter.h" +#include "Platform/Unix/Pipe.h" +#include "Platform/Unix/Poller.h" + +namespace TrueCrypt +{ +	string Process::Execute (const string &processName, const list <string> &arguments, int timeOut, ProcessExecFunctor *execFunctor, const Buffer *inputData) +	{ +		char *args[32]; +		if (array_capacity (args) <= arguments.size()) +			throw ParameterTooLarge (SRC_POS); + +#if 0 +		stringstream dbg; +		dbg << "exec " << processName; +		foreach (const string &at, arguments) +			dbg << " " << at; +		trace_msg (dbg.str()); +#endif + +		Pipe inPipe, outPipe, errPipe, exceptionPipe; + +		int forkedPid = fork(); +		throw_sys_if (forkedPid == -1); + +		if (forkedPid == 0) +		{ +			try +			{ +				try +				{ +					int argIndex = 0; +					if (!execFunctor) +						args[argIndex++] = const_cast <char*> (processName.c_str()); + +					foreach (const string &arg, arguments) +					{ +						args[argIndex++] = const_cast <char*> (arg.c_str()); +					} +					args[argIndex] = nullptr; + +					if (inputData) +					{ +						throw_sys_if (dup2 (inPipe.GetReadFD(), STDIN_FILENO) == -1); +					} +					else +					{ +						inPipe.Close(); +						int nullDev = open ("/dev/null", 0); +						throw_sys_sub_if (nullDev == -1, "/dev/null"); +						throw_sys_if (dup2 (nullDev, STDIN_FILENO) == -1); +					} + +					throw_sys_if (dup2 (outPipe.GetWriteFD(), STDOUT_FILENO) == -1); +					throw_sys_if (dup2 (errPipe.GetWriteFD(), STDERR_FILENO) == -1); +					exceptionPipe.GetWriteFD(); + +					if (execFunctor) +					{ +						(*execFunctor)(argIndex, args); +					} +					else +					{ +						execvp (args[0], args); +						throw SystemException (SRC_POS, args[0]); +					} +				} +				catch (Exception &) +				{ +					throw; +				} +				catch (exception &e) +				{ +					throw ExternalException (SRC_POS, StringConverter::ToExceptionString (e)); +				} +				catch (...) +				{ +					throw UnknownException (SRC_POS); +				} +			} +			catch (Exception &e) +			{ +				try +				{ +					shared_ptr <Stream> outputStream (new FileStream (exceptionPipe.GetWriteFD())); +					e.Serialize (outputStream); +				} +				catch (...) { } +			} + +			_exit (1); +		} + +		throw_sys_if (fcntl (outPipe.GetReadFD(), F_SETFL, O_NONBLOCK) == -1); +		throw_sys_if (fcntl (errPipe.GetReadFD(), F_SETFL, O_NONBLOCK) == -1); +		throw_sys_if (fcntl (exceptionPipe.GetReadFD(), F_SETFL, O_NONBLOCK) == -1); + +		vector <char> buffer (4096), stdOutput (4096), errOutput (4096), exOutput (4096); +		buffer.clear (); +		stdOutput.clear (); +		errOutput.clear (); +		exOutput.clear (); + +		Poller poller (outPipe.GetReadFD(), errPipe.GetReadFD(), exceptionPipe.GetReadFD()); +		int status, waitRes; + +		if (inputData) +			throw_sys_if (write (inPipe.GetWriteFD(), inputData->Ptr(), inputData->Size()) == -1 && errno != EPIPE); + +		inPipe.Close(); + +		int timeTaken = 0; +		do +		{ +			const int pollTimeout = 200; +			try +			{ +				ssize_t bytesRead = 0; +				foreach (int fd, poller.WaitForData (pollTimeout)) +				{ +					bytesRead = read (fd, &buffer[0], buffer.capacity()); +					if (bytesRead > 0) +					{ +						if (fd == outPipe.GetReadFD()) +							stdOutput.insert (stdOutput.end(), buffer.begin(), buffer.begin() + bytesRead); +						else if (fd == errPipe.GetReadFD()) +							errOutput.insert (errOutput.end(), buffer.begin(), buffer.begin() + bytesRead); +						else if (fd == exceptionPipe.GetReadFD()) +							exOutput.insert (exOutput.end(), buffer.begin(), buffer.begin() + bytesRead); +					} +				} + +				if (bytesRead == 0) +				{ +					waitRes = waitpid (forkedPid, &status, 0); +					break; +				} +			} +			catch (TimeOut&) +			{ +				timeTaken += pollTimeout; +				if (timeOut >= 0 && timeTaken >= timeOut) +					throw; +			} +		} while ((waitRes = waitpid (forkedPid, &status, WNOHANG)) == 0); +		throw_sys_if (waitRes == -1); + +		if (!exOutput.empty()) +		{ +			auto_ptr <Serializable> deserializedObject; +			Exception *deserializedException = nullptr; + +			try +			{ +				shared_ptr <Stream> stream (new MemoryStream (ConstBufferPtr ((byte *) &exOutput[0], exOutput.size()))); +				deserializedObject.reset (Serializable::DeserializeNew (stream)); +				deserializedException = dynamic_cast <Exception*> (deserializedObject.get()); +			} +			catch (...)	{ } + +			if (deserializedException) +				deserializedException->Throw(); +		} + +		int exitCode = (WIFEXITED (status) ? WEXITSTATUS (status) : 1); +		if (exitCode != 0) +		{ +			string strErrOutput; + +			if (!errOutput.empty()) +				strErrOutput.insert (strErrOutput.begin(), errOutput.begin(), errOutput.end()); + +			throw ExecutedProcessFailed (SRC_POS, processName, exitCode, strErrOutput); +		} + +		string strOutput; + +		if (!stdOutput.empty()) +			strOutput.insert (strOutput.begin(), stdOutput.begin(), stdOutput.end()); + +		return strOutput; +	} +} | 
