From aa4d426b4d3527d7e166df1a05058c9a4a0f6683 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 30 Apr 2021 00:33:56 +0200 Subject: initial/final commit --- .../vmime/platforms/posix/posixChildProcess.cpp | 410 +++++++++++++++++++++ 1 file changed, 410 insertions(+) create mode 100644 vmime-master/src/vmime/platforms/posix/posixChildProcess.cpp (limited to 'vmime-master/src/vmime/platforms/posix/posixChildProcess.cpp') diff --git a/vmime-master/src/vmime/platforms/posix/posixChildProcess.cpp b/vmime-master/src/vmime/platforms/posix/posixChildProcess.cpp new file mode 100644 index 0000000..4a9fef3 --- /dev/null +++ b/vmime-master/src/vmime/platforms/posix/posixChildProcess.cpp @@ -0,0 +1,410 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "vmime/config.hpp" + + +#if VMIME_PLATFORM_IS_POSIX && VMIME_HAVE_FILESYSTEM_FEATURES + + +#include "vmime/platforms/posix/posixChildProcess.hpp" +#include "vmime/platforms/posix/posixFile.hpp" + +#include "vmime/exception.hpp" + +#include +#include +#include +#include +#include +#include + + +namespace vmime { +namespace platforms { +namespace posix { + + +// posixChildProcessFactory + +shared_ptr posixChildProcessFactory::create(const utility::file::path& path) const { + + return make_shared (path); +} + + + +#ifndef VMIME_BUILDING_DOC + + +// getPosixSignalMessage +// Returns the name of a POSIX signal. + +static const string getPosixSignalMessage(const int num) { + + switch (num) { + case SIGHUP: return "SIGHUP"; + case SIGINT: return "SIGINT"; + case SIGQUIT: return "SIGQUIT"; + case SIGILL: return "SIGILL"; + case SIGABRT: return "SIGABRT"; + case SIGFPE: return "SIGFPE"; + case SIGKILL: return "SIGKILL"; + case SIGSEGV: return "SIGSEGV"; + case SIGPIPE: return "SIGPIPE"; + case SIGALRM: return "SIGALRM"; + case SIGTERM: return "SIGTERM"; + case SIGUSR1: return "SIGUSR1"; + case SIGUSR2: return "SIGUSR2"; + case SIGCHLD: return "SIGCHLD"; + case SIGCONT: return "SIGCONT"; + case SIGSTOP: return "SIGSTOP"; + case SIGTSTP: return "SIGTSTP"; + case SIGTTIN: return "SIGTTIN"; + case SIGTTOU: return "SIGTTOU"; + } + + return "(unknown)"; +} + + +// getPosixErrorMessage +// Returns a message corresponding to an error code. + +static const string getPosixErrorMessage(const int num) { + +#ifdef strerror_r + char res[256]; + res[0] = '\0'; + + strerror_r(num, res, sizeof(res)); + + return string(res); +#else + return string(strerror(num)); +#endif + +} + + +// Output stream adapter for POSIX pipe + +class outputStreamPosixPipeAdapter : public utility::outputStream { + +public: + + outputStreamPosixPipeAdapter(const int desc) + : m_desc(desc) { + + } + + void flush() { + + ::fsync(m_desc); + } + +protected: + + void writeImpl(const byte_t* const data, const size_t count) { + + if (::write(m_desc, data, count) == -1) { + const string errorMsg = getPosixErrorMessage(errno); + throw exceptions::system_error(errorMsg); + } + } + +private: + + const int m_desc; +}; + + +// Input stream adapter for POSIX pipe + +class inputStreamPosixPipeAdapter : public utility::inputStream { + +public: + + inputStreamPosixPipeAdapter(const int desc) + : m_desc(desc) { + } + + bool eof() const { + + return m_eof; + } + + void reset() { + + // Do nothing: unsupported + } + + size_t skip(const size_t count) { + + // TODO: not tested + byte_t buffer[4096]; + + ssize_t bytesSkipped = 0; + ssize_t bytesRead = 0; + + while ((bytesRead = ::read(m_desc, buffer, + std::min(sizeof(buffer), count - bytesSkipped))) != 0) { + + if (bytesRead == -1) { + const string errorMsg = getPosixErrorMessage(errno); + throw exceptions::system_error(errorMsg); + } + + bytesSkipped += bytesRead; + } + + return static_cast (bytesSkipped); + } + + size_t read(byte_t* const data, const size_t count) { + + ssize_t bytesRead = 0; + + if ((bytesRead = ::read(m_desc, data, count)) == -1) { + const string errorMsg = getPosixErrorMessage(errno); + throw exceptions::system_error(errorMsg); + } + + m_eof = (bytesRead == 0); + + return static_cast (bytesRead); + } + +private: + + const int m_desc; + + bool m_eof; +}; + + +#endif // VMIME_BUILDING_DOC + + + +// posixChildProcess + +posixChildProcess::posixChildProcess(const utility::file::path& path) + : m_processPath(path), + m_started(false), + m_stdIn(null), + m_stdOut(null), + m_pid(0), + m_argArray(NULL) { + + m_pipe[0] = 0; + m_pipe[1] = 0; + + sigemptyset(&m_oldProcMask); +} + + +posixChildProcess::~posixChildProcess() { + + if (m_started) { + sigprocmask(SIG_SETMASK, &m_oldProcMask, NULL); + } + + if (m_pipe[0] != 0) { + close(m_pipe[0]); + } + + if (m_pipe[1] != 0) { + close(m_pipe[1]); + } + + delete [] m_argArray; +} + + +// The following code is highly inspired and adapted from the 'sendmail' +// provider module in Evolution data server code. +// +// Original authors: Dan Winship +// Copyright 2000 Ximian, Inc. (www.ximian.com) + +void posixChildProcess::start(const std::vector & args, const int flags) { + + if (m_started) { + return; + } + + // Construct C-style argument array + const char** argv = new const char*[args.size() + 2]; + + m_argVector = args; // for c_str() pointer to remain valid + m_argArray = argv; // to free later + + argv[0] = m_processPath.getLastComponent().getBuffer().c_str(); + argv[args.size() + 1] = NULL; + + for (unsigned int i = 0 ; i < m_argVector.size() ; ++i) { + argv[i + 1] = m_argVector[i].c_str(); + } + + // Create a pipe to communicate with the child process + int fd[2]; + + if (pipe(fd) == -1) { + throw exceptions::system_error(getPosixErrorMessage(errno)); + } + + m_pipe[0] = fd[0]; + m_pipe[1] = fd[1]; + + // Block SIGCHLD so the calling application doesn't notice + // process exiting before we do + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + sigprocmask(SIG_BLOCK, &mask, &m_oldProcMask); + + // Spawn process + const pid_t pid = fork(); + + if (pid == -1) { // error + + const string errorMsg = getPosixErrorMessage(errno); + + sigprocmask(SIG_SETMASK, &m_oldProcMask, NULL); + + close(fd[0]); + close(fd[1]); + + throw exceptions::system_error(errorMsg); + + } else if (pid == 0) { // child process + + if (flags & FLAG_REDIRECT_STDIN) { + dup2(fd[0], STDIN_FILENO); + } else { + close(fd[0]); + } + + if (flags & FLAG_REDIRECT_STDOUT) { + dup2(fd[1], STDOUT_FILENO); + } else { + close(fd[1]); + } + + posixFileSystemFactory* pfsf = new posixFileSystemFactory(); + + const string path = pfsf->pathToString(m_processPath); + + delete pfsf; + + execv(path.c_str(), const_cast (argv)); + _exit(255); + } + + if (flags & FLAG_REDIRECT_STDIN) { + + m_stdIn = make_shared (m_pipe[1]); + + } else { + + close(m_pipe[1]); + m_pipe[1] = 0; + } + + if (flags & FLAG_REDIRECT_STDOUT) { + + m_stdOut = make_shared (m_pipe[0]); + + } else { + + close(m_pipe[0]); + m_pipe[0] = 0; + } + + m_pid = pid; + m_started = true; +} + + +shared_ptr posixChildProcess::getStdIn() { + + return m_stdIn; +} + + +shared_ptr posixChildProcess::getStdOut() { + + return m_stdOut; +} + + +void posixChildProcess::waitForFinish() { + + // Close stdin pipe + if (m_pipe[1] != 0) { + close(m_pipe[1]); + m_pipe[1] = 0; + } + + int wstat; + + while (waitpid(m_pid, &wstat, 0) == -1 && errno == EINTR) { + ; + } + + if (!WIFEXITED(wstat)) { + + throw exceptions::system_error("Process exited with signal " + + getPosixSignalMessage(WTERMSIG(wstat))); + + } else if (WEXITSTATUS(wstat) != 0) { + + if (WEXITSTATUS(wstat) == 255) { + + scoped_ptr pfsf(new posixFileSystemFactory()); + + throw exceptions::system_error("Could not execute '" + + pfsf->pathToString(m_processPath) + "'"); + + } else { + + std::ostringstream oss; + oss.imbue(std::locale::classic()); + + oss << "Process exited with status " << WEXITSTATUS(wstat); + + throw exceptions::system_error(oss.str()); + } + } +} + + +} // posix +} // platforms +} // vmime + + +#endif // VMIME_PLATFORM_IS_POSIX && VMIME_HAVE_FILESYSTEM_FEATURES + -- cgit v1.2.3