Program Listing for File fifo_writer.cpp

Return to documentation for file (monitoring/fifo_writer.cpp)

#include "fifo_writer.hpp"

#include <sys/stat.h>
#include <ers/Severity.h>

FIFOWriter::FIFOWriter(const std::string& fifoname) {
    try{
        validate_fifo(fifoname);
    } catch(const monitoring_log::fifo_issue &er) {
        ers::error(er);
        return;
    }

    signal(SIGPIPE, SIG_IGN);

    // Opening a FIFO for write-only will fail if there is no reader, so we fake a reader
    int tmpfd = open(fifoname.c_str(), O_RDONLY | O_NDELAY);
    m_fifo_fd = open(fifoname.c_str(), O_WRONLY | O_NDELAY | O_NONBLOCK);
    if (m_fifo_fd < 0) {
        ers::error(monitoring_log::fifo_issue(std::format("Cannot open monitoring FIFO %s, error %d: %s", fifoname, errno, strerror(errno))));
    }
    ERS_INFO(std::format("Monitoring FIFO {} opened, fd={}", fifoname, m_fifo_fd));
    fcntl(m_fifo_fd, F_SETPIPE_SZ, 1048576);
    close(tmpfd);
}

FIFOWriter::~FIFOWriter() {
    close(m_fifo_fd);
}

void FIFOWriter::write_message(const nlohmann::json& message) {
    if (m_fifo_fd == -1) {
        return;
    }
    const std::string msg = std::format("{}\n", message.dump());
    const char* msg_ptr = msg.c_str();
    size_t bytes_written = 0;

    while (bytes_written < msg.size()) {
        ssize_t result = write(m_fifo_fd, msg_ptr + bytes_written, msg.size() - bytes_written);
        if (result == -1) {
            if (errno == EINTR) {
                continue;
            } else if (errno == EPIPE) {
                break;
            }
            ers::warning(monitoring_log::fifo_issue(std::format("Error writing to FIFO. Code {}: {}", errno, strerror(errno))));
            break;
        }
        bytes_written += result;
    }
}

void FIFOWriter::validate_fifo(const std::string& fifoname) {
    if (fifoname.empty()) {
        m_fifo_fd = -1;
        throw monitoring_log::fifo_issue("FIFO path is empty");
    }

    // Check if file exists and is a FIFO
    struct stat file_stat;
    if (stat(fifoname.c_str(), &file_stat) == -1) {
        m_fifo_fd = -1;
        throw monitoring_log::fifo_issue(std::format("Cannot access FIFO %s, error %d: %s",
                   fifoname, errno, strerror(errno)));
    }

    if (not S_ISFIFO(file_stat.st_mode)) {
        m_fifo_fd = -1;
        throw monitoring_log::fifo_issue(std::format("%s exists but is not a UNIX FIFO", fifoname));
    }
}