diff options
Diffstat (limited to 'plugins/fcgi/fastcgiprocess.cpp')
-rw-r--r-- | plugins/fcgi/fastcgiprocess.cpp | 151 |
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); +} + |