summaryrefslogtreecommitdiffhomepage
path: root/plugins/fcgi/fastcgiprocess.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/fcgi/fastcgiprocess.cpp')
-rw-r--r--plugins/fcgi/fastcgiprocess.cpp151
1 files changed, 151 insertions, 0 deletions
diff --git a/plugins/fcgi/fastcgiprocess.cpp b/plugins/fcgi/fastcgiprocess.cpp
new file mode 100644
index 0000000..dd51583
--- /dev/null
+++ b/plugins/fcgi/fastcgiprocess.cpp
@@ -0,0 +1,151 @@
+#include "fastcgiprocess.h"
+
+#include <chrono>
+#include <filesystem>
+#include <iostream>
+#include <string>
+#include <thread>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/beast/core.hpp>
+#include <boost/beast/http.hpp>
+#include <boost/beast/websocket.hpp>
+#include <boost/beast/websocket/ssl.hpp>
+#include <boost/beast/ssl.hpp>
+#include <boost/beast/version.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/asio/buffers_iterator.hpp>
+#include <boost/asio/connect.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/ssl/error.hpp>
+#include <boost/asio/ssl/stream.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/xml_parser.hpp>
+#include <boost/asio/local/stream_protocol.hpp>
+
+#include <signal.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <libreichwein/file.h>
+#include <libreichwein/process.h>
+
+using namespace std::string_literals;
+namespace fs = std::filesystem;
+namespace pt = boost::property_tree;
+using namespace Reichwein;
+
+#define FCGI_LISTENSOCK_FILENO 0
+
+FastCGIProcess::FastCGIProcess(const std::filesystem::path& exe_path, const std::string& host, unsigned short port):
+ m_pid{},
+ m_command{exe_path.generic_string()},
+ m_host{host},
+ m_port{port}
+{
+ start();
+}
+
+FastCGIProcess::FastCGIProcess(const std::filesystem::path& exe_path, const fs::path& socket_path):
+ m_pid{},
+ m_command{exe_path.generic_string()},
+ m_socket_path{socket_path}
+{
+ start();
+}
+
+FastCGIProcess::~FastCGIProcess()
+{
+ stop();
+}
+
+void FastCGIProcess::start()
+{
+ if (m_pid != 0)
+ throw std::runtime_error("Process already running, so it can't be started");
+
+ m_pid = fork();
+ if (m_pid < 0)
+ throw std::runtime_error("Fork unsuccessful.");
+
+ if (m_pid == 0) { // child process branch
+ try {
+ int fd{};
+ boost::asio::io_context ioc;
+ boost::asio::ip::tcp::resolver resolver(ioc);
+ boost::asio::ip::tcp::acceptor acceptor(ioc);
+ boost::asio::local::stream_protocol::acceptor file_acceptor(ioc);
+
+ if (m_socket_path.empty()) { // tcp connection
+ auto const results = resolver.resolve(m_host.c_str(), std::to_string(m_port).c_str());
+ if (results.begin() == results.end())
+ std::runtime_error("no resolve result");
+ boost::asio::ip::tcp::endpoint endpoint{*results.begin()};
+ acceptor.open(endpoint.protocol());
+ acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
+ acceptor.bind(endpoint);
+ acceptor.listen();
+ fd = acceptor.native_handle();
+ } else { // unix domain socket
+ std::error_code ec;
+ fs::remove(m_socket_path, ec); // otherwise we get: "bind: Address already in use"
+
+ boost::asio::local::stream_protocol::endpoint endpoint(m_socket_path);
+ file_acceptor.open(endpoint.protocol());
+ file_acceptor.set_option(boost::asio::local::stream_protocol::acceptor::reuse_address(true));
+ file_acceptor.bind(endpoint);
+ file_acceptor.listen();
+ fd = file_acceptor.native_handle();
+ }
+
+ if (fd != FCGI_LISTENSOCK_FILENO) {
+ close(FCGI_LISTENSOCK_FILENO);
+ dup2(fd, FCGI_LISTENSOCK_FILENO);
+ close(fd);
+ }
+
+ execl(m_command.c_str(), m_command.c_str(), (const char*)nullptr);
+ } catch (const std::exception& ex) {
+ std::cout << "FastCGI process error: " << ex.what() << std::endl;
+ }
+ exit(0);
+ }
+
+ // wait for server to start up
+ if (m_socket_path.empty()) { // tcp connection
+ Process::wait_for_pid_listening_on(m_pid, m_port);
+ } else { // unix domain socket
+ Process::wait_for_pid_listening_on(m_pid, m_socket_path);
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+}
+
+void FastCGIProcess::stop()
+{
+ if (m_pid == 0)
+ throw std::runtime_error("Process not running, so it can't be stopped");
+
+ if (kill(m_pid, SIGTERM) != 0)
+ throw std::runtime_error("Unable to kill process");
+
+ if (int result = waitpid(m_pid, NULL, 0); result != m_pid)
+ throw std::runtime_error("waitpid returned "s + std::to_string(result));
+
+ if (!m_socket_path.empty()) {
+ std::error_code ec;
+ fs::remove(m_socket_path, ec);
+ }
+
+ m_pid = 0;
+}
+
+bool FastCGIProcess::is_running()
+{
+ if (m_pid == 0)
+ return false;
+
+ return Process::is_running(m_pid);
+}
+