File 0005-DRBDmon-Add-drbd-events-log-supplier.patch of Package drbd-utils

From 60c3ed545e1c36de25545656f5c6c586e548b852 Mon Sep 17 00:00:00 2001
From: Robert Altnoeder <robert.altnoeder@linbit.com>
Date: Wed, 20 Nov 2024 18:44:41 +0100
Subject: [PATCH 05/12] DRBDmon: Add drbd-events-log-supplier

---
 user/drbdmon/drbd-events-log-supplier.cpp | 147 ++++++++++++++++++++++
 1 file changed, 147 insertions(+)
 create mode 100644 user/drbdmon/drbd-events-log-supplier.cpp

diff --git a/user/drbdmon/drbd-events-log-supplier.cpp b/user/drbdmon/drbd-events-log-supplier.cpp
new file mode 100644
index 000000000000..9fe48f0ccd71
--- /dev/null
+++ b/user/drbdmon/drbd-events-log-supplier.cpp
@@ -0,0 +1,147 @@
+#include <cstddef>
+#include <iostream>
+#include <fstream>
+#include <string>
+
+extern "C"
+{
+    #include <unistd.h>
+}
+
+constexpr int ERR_OUT_OF_MEMORY     = 2;
+constexpr int ERR_IO                = 3;
+
+const std::string INIT_STATE_SEPA("exists -");
+const size_t      INIT_STATE_SEPA_LENGTH = INIT_STATE_SEPA.length();
+
+int process_events(const std::string& path);
+bool is_init_state_sepa(const std::string& event_line);
+
+/**
+ * DRBD events log file supplier for DRBDmon
+ */
+int main(int argc, char* argv[])
+{
+    int exit_code = EXIT_FAILURE;
+
+    try
+    {
+        if (argc == 2)
+        {
+            std::string path(argv[1]);
+            exit_code = process_events(path);
+        }
+        else
+        {
+            std::cerr << "DRBD Events Log File Supplier | Arguments:\n" <<
+                "    path .......... Path to a file containing DBRD event lines\n";
+        }
+    }
+    catch (std::bad_alloc&)
+    {
+        std::cerr << "Out of memory" << std::endl;
+        exit_code = ERR_OUT_OF_MEMORY;
+    }
+
+    std::cout << std::flush;
+    std::cerr << std::flush;
+
+    return exit_code;
+}
+
+/**
+ * Emits event lines from an events log file and appends the initial state separator
+ * if it is absent from the file. The output is stdout, which DRBDmon pipes to itself.
+ *
+ * DRBDmon recovers its events source process by respawning it if it exits for any reason.
+ * Therefore, this method does not return if successful, instead, it will
+ * suspend indefinitely. DRBDmon will terminate this process when the user exits DRBDmon.
+ */
+int process_events(const std::string& path)
+{
+    int rc = ERR_IO;
+
+    // Internal pipe used for suspending execution
+    int pipe_fd[2];
+    int pipe_rc = pipe(pipe_fd);
+    if (pipe_rc == 0)
+    {
+        std::ifstream in_file(path);
+        if (in_file.good())
+        {
+            bool have_init_state_sepa = false;
+
+            // Emit events from the file to DRBDmon
+            std::string event_line;
+            while (in_file.good())
+            {
+                std::getline(in_file, event_line);
+                if (event_line.length() > 0)
+                {
+                    std::cout << event_line << '\n';
+                }
+                if (is_init_state_sepa(event_line))
+                {
+                    have_init_state_sepa = true;
+                }
+
+                event_line.clear();
+            }
+
+            if (in_file.eof())
+            {
+                // If there was no initial state separator in the events log file,
+                // emit one to DRBDmon
+                if (!have_init_state_sepa)
+                {
+                    std::cout << INIT_STATE_SEPA << '\n';
+                }
+                std::cout << std::flush;
+
+                rc = EXIT_SUCCESS;
+
+                // Suspend execution indefinitely. DRBDmon will terminate this process when it exits.
+                char in_char = 0;
+                const ssize_t read_count = read(pipe_fd[0], &in_char, 1);
+                // Nothing to do with that result...
+                static_cast<void> (read_count);
+            }
+            else
+            {
+                // I/O failed before the entire file was processed, I/O error.
+                std::cerr << "I/O error while reading DRBD events file \"" << path << "\"\n";
+            }
+        }
+        else
+        {
+            // Cannot open/read the file. Typically a non-existent path/file or permissions problem.
+            std::cerr << "I/O error, cannot read DRBD events file \"" << path << "\"\n";
+        }
+    }
+    else
+    {
+        // Cannot create the pipe used for suspending.
+        // Typically a resource exhaustion problem, e.g. the OS being out of memory.
+        std::cerr << "I/O error, pipe creation failed\n";
+    }
+
+    return rc;
+}
+
+/**
+ * Checks whether an events line is the initial state separator
+ */
+bool is_init_state_sepa(const std::string& event_line)
+{
+    bool result = false;
+    if (event_line.length() >= INIT_STATE_SEPA_LENGTH)
+    {
+        size_t idx = 0;
+        while (idx < INIT_STATE_SEPA_LENGTH && event_line[idx] == INIT_STATE_SEPA[idx])
+        {
+            ++idx;
+        }
+        result = idx == INIT_STATE_SEPA_LENGTH;
+    }
+    return result;
+}
-- 
2.43.0

openSUSE Build Service is sponsored by