TYPE3
[iec.git] / src / type3_AndroidCloud / anbox-master / external / process-cpp-minimal / src / core / posix / child_process.cpp
diff --git a/src/type3_AndroidCloud/anbox-master/external/process-cpp-minimal/src/core/posix/child_process.cpp b/src/type3_AndroidCloud/anbox-master/external/process-cpp-minimal/src/core/posix/child_process.cpp
new file mode 100644 (file)
index 0000000..ad16571
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * Copyright © 2013 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
+ */
+
+#include <core/posix/child_process.h>
+
+#ifndef ANDROID
+#include <boost/iostreams/device/file_descriptor.hpp>
+#include <boost/iostreams/stream.hpp>
+#endif
+
+#include <atomic>
+#include <fstream>
+#include <mutex>
+#include <unordered_map>
+
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include <sys/eventfd.h>
+#include <sys/signalfd.h>
+
+#include <assert.h>
+
+#include <sys/wait.h>
+
+#ifndef ANDROID
+namespace io = boost::iostreams;
+#endif
+
+namespace
+{
+
+struct DeathObserverImpl : public core::posix::ChildProcess::DeathObserver
+{
+    DeathObserverImpl(const std::shared_ptr<core::posix::SignalTrap>& trap)
+        : on_sig_child_connection
+          {
+              trap->signal_raised().connect([this](core::posix::Signal signal)
+              {
+                  switch (signal)
+                  {
+                  case core::posix::Signal::sig_chld:
+                    on_sig_child();
+                    break;
+                  default:
+                    break;
+                  }
+              })
+          }
+    {
+        if (!trap->has(core::posix::Signal::sig_chld))
+            throw std::logic_error(
+                    "DeathObserver::DeathObserverImpl: Given SignalTrap"
+                    " instance does not trap Signal::sig_chld.");
+    }
+
+    bool add(const core::posix::ChildProcess& process) override
+    {
+        if (process.pid() == -1)
+            return false;
+
+        std::lock_guard<std::mutex> lg(guard);
+
+        bool added = false;
+        auto new_process = std::make_pair(process.pid(), process);
+        std::tie(std::ignore, added) = children.insert(new_process);
+
+        if (added)
+        {
+            // The process may have died between it's instantiation and it
+            // being added to the children map. Check that it's still alive.
+            int status{-1};
+            if (::waitpid(process.pid(), &status, WNOHANG) != 0) // child no longer alive
+            {
+                // we missed the SIGCHLD signal so we must now manually
+                // inform our subscribers.
+                signals.child_died(new_process.second);
+                children.erase(new_process.first);
+                return false;
+            }
+        }
+
+        return added;
+    }
+
+    bool has(const core::posix::ChildProcess& process) const override
+    {
+        std::lock_guard<std::mutex> lg(guard);
+        return children.count(process.pid()) > 0;
+    }
+
+    const core::Signal<core::posix::ChildProcess>& child_died() const override
+    {
+        return signals.child_died;
+    }
+
+    void on_sig_child() override
+    {
+        pid_t pid{-1}; int status{-1};
+        while (true)
+        {
+            pid = ::waitpid(0, &status, WNOHANG);
+
+            if (pid == -1)
+            {
+                if (errno == ECHILD)
+                {
+                    break; // No more children
+                }
+                continue; // Ignore stray SIGCHLD signals
+            }
+            else if (pid == 0)
+            {
+                break; // No more children
+            }
+            else
+            {
+                std::lock_guard<std::mutex> lg(guard);
+                auto it = children.find(pid);
+
+                if (it != children.end())
+                {
+                    if (WIFSIGNALED(status) || WIFEXITED(status))
+                    {
+                        signals.child_died(it->second);
+                        children.erase(it);
+                    }
+                }
+            }
+        }
+    }
+
+    mutable std::mutex guard;
+    std::unordered_map<pid_t, core::posix::ChildProcess> children;
+    core::ScopedConnection on_sig_child_connection;
+    struct
+    {
+        core::Signal<core::posix::ChildProcess> child_died;
+    } signals;
+};
+}
+
+std::unique_ptr<core::posix::ChildProcess::DeathObserver>
+core::posix::ChildProcess::DeathObserver::create_once_with_signal_trap(
+        std::shared_ptr<core::posix::SignalTrap> trap)
+{
+    static std::atomic<bool> has_been_created_once{false};
+
+    if (has_been_created_once.exchange(true))
+        throw std::runtime_error
+        {
+            "DeathObserver::create_once_with_signal_trap: "
+            "Cannot create more than one instance."
+        };
+
+    try
+    {
+        std::unique_ptr<core::posix::ChildProcess::DeathObserver> result
+        {
+            new DeathObserverImpl{trap}
+        };
+
+        return result;
+    } catch(...)
+    {
+        // We make sure that a throwing c'tor does not impact our ability to
+        // retry creation of a DeathObserver instance.
+        has_been_created_once.store(false);
+
+        std::rethrow_exception(std::current_exception());
+    }
+
+    assert(false && "We should never reach here.");
+
+    // Silence the compiler.
+    return std::unique_ptr<core::posix::ChildProcess::DeathObserver>{};
+}
+
+namespace core
+{
+namespace posix
+{
+ChildProcess::Pipe ChildProcess::Pipe::invalid()
+{
+    static Pipe p;
+    static std::once_flag flag;
+
+    std::call_once(flag, [&]() { p.close_read_fd(); p.close_write_fd(); });
+
+    return p;
+}
+
+ChildProcess::Pipe::Pipe()
+{
+    int rc = ::pipe(fds);
+
+    if (rc == -1)
+        throw std::system_error(errno, std::system_category());
+}
+
+ChildProcess::Pipe::Pipe(const ChildProcess::Pipe& rhs) : fds{-1, -1}
+{
+    if (rhs.fds[0] != -1)
+        fds[0] = ::dup(rhs.fds[0]);
+
+    if (rhs.fds[1] != -1)
+        fds[1] = ::dup(rhs.fds[1]);
+}
+
+ChildProcess::Pipe::~Pipe()
+{
+    if (fds[0] != -1)
+        ::close(fds[0]);
+    if (fds[1] != -1)
+        ::close(fds[1]);
+}
+
+int ChildProcess::Pipe::read_fd() const
+{
+    return fds[0];
+}
+
+void ChildProcess::Pipe::close_read_fd()
+{
+    if (fds[0] != -1)
+    {
+        ::close(fds[0]);
+        fds[0] = -1;
+    }
+}
+
+int ChildProcess::Pipe::write_fd() const
+{
+    return fds[1];
+}
+
+void ChildProcess::Pipe::close_write_fd()
+{
+    if (fds[1] != -1)
+    {
+        ::close(fds[1]);
+        fds[1] = -1;
+    }
+}
+
+ChildProcess::Pipe& ChildProcess::Pipe::operator=(const ChildProcess::Pipe& rhs)
+{
+    if (fds[0] != -1)
+        ::close(fds[0]);
+    if (fds[1] != -1)
+        ::close(fds[1]);
+
+    if (rhs.fds[0] != -1)
+        fds[0] = ::dup(rhs.fds[0]);
+    else
+        fds[0] = -1;
+    if (rhs.fds[1] != -1)
+        fds[1] = ::dup(rhs.fds[1]);
+    else
+        fds[1] = -1;
+
+    return *this;
+}
+
+struct ChildProcess::Private
+{
+    // stdin and stdout are always "relative" to the childprocess, i.e., we
+    // write to stdin of the child process and read from its stdout.
+    Private(pid_t pid,
+            const ChildProcess::Pipe& stderr,
+            const ChildProcess::Pipe& stdin,
+            const ChildProcess::Pipe& stdout)
+        : pipes{stderr, stdin, stdout},
+#ifndef ANDROID
+          serr(pipes.stderr.read_fd(), io::never_close_handle),
+          sin(pipes.stdin.write_fd(), io::never_close_handle),
+          sout(pipes.stdout.read_fd(), io::never_close_handle),
+          cerr(&serr),
+          cin(&sin),
+          cout(&sout),
+#endif
+          original_parent_pid(::getpid()),
+          original_child_pid(pid)
+    {
+    }
+
+    ~Private()
+    {
+        // Check if we are in the original parent process.
+        if (original_parent_pid == getpid() && !dont_kill_on_cleanup)
+        {
+            // If so, check if we are considering a valid pid here.
+            // If so, we kill the original child.
+            if (original_child_pid != -1)
+                ::kill(original_child_pid, SIGKILL);
+        }
+    }
+
+    struct
+    {
+        ChildProcess::Pipe stdin;
+        ChildProcess::Pipe stdout;
+        ChildProcess::Pipe stderr;
+    } pipes;
+
+#ifndef ANDROID
+    io::stream_buffer<io::file_descriptor_source> serr;
+    io::stream_buffer<io::file_descriptor_sink> sin;
+    io::stream_buffer<io::file_descriptor_source> sout;
+    std::istream cerr;
+    std::ostream cin;
+    std::istream cout;
+#endif
+
+    // We need to store the original parent pid as we might have been forked
+    // and with our automatic cleanup in place, it might happen that the d'tor
+    // is called from the child process.
+    pid_t original_parent_pid;
+    pid_t original_child_pid;
+
+    bool dont_kill_on_cleanup = false;
+};
+
+ChildProcess ChildProcess::invalid()
+{
+    // We take the init process as child.
+    static const pid_t invalid_pid = 1;
+    return ChildProcess(invalid_pid, Pipe::invalid(), Pipe::invalid(), Pipe::invalid());
+}
+
+ChildProcess::ChildProcess(pid_t pid,
+                           const ChildProcess::Pipe& stdin_pipe,
+                           const ChildProcess::Pipe& stdout_pipe,
+                           const ChildProcess::Pipe& stderr_pipe)
+    : Process(pid),
+      d(new Private{pid, stdin_pipe, stdout_pipe, stderr_pipe})
+{
+}
+
+ChildProcess::~ChildProcess()
+{
+}
+
+wait::Result ChildProcess::wait_for(const wait::Flags& flags)
+{
+    int status = -1;
+    pid_t result_pid = ::waitpid(pid(), std::addressof(status), static_cast<int>(flags));
+
+    if (result_pid == -1)
+        throw std::system_error(errno, std::system_category());
+
+    wait::Result result;
+
+    if (result_pid == 0)
+    {
+        result.status = wait::Result::Status::no_state_change;
+        return result;
+    }
+
+    if (WIFEXITED(status))
+    {
+        result.status = wait::Result::Status::exited;
+        result.detail.if_exited.status = static_cast<exit::Status>(WEXITSTATUS(status));
+    } else if (WIFSIGNALED(status))
+    {
+        result.status = wait::Result::Status::signaled;
+        result.detail.if_signaled.signal = static_cast<Signal>(WTERMSIG(status));
+        result.detail.if_signaled.core_dumped = WCOREDUMP(status);
+    } else if (WIFSTOPPED(status))
+    {
+        result.status = wait::Result::Status::stopped;
+        result.detail.if_stopped.signal = static_cast<Signal>(WSTOPSIG(status));
+    }
+#ifndef ANDROID
+    else if (WIFCONTINUED(status))
+    {
+        result.status = wait::Result::Status::continued;
+    }
+#endif
+
+    return result;
+}
+
+void ChildProcess::dont_kill_on_cleanup()
+{
+  d->dont_kill_on_cleanup = true;
+}
+
+#ifndef ANDROID
+std::istream& ChildProcess::cerr()
+{
+    return d->cerr;
+}
+
+std::ostream& ChildProcess::cin()
+{
+    return d->cin;
+}
+
+std::istream& ChildProcess::cout()
+{
+    return d->cout;
+}
+#endif
+}
+}