Added IPC support to attention handler

Created a listener thread that configures and starts the attention
gpio monitor. The listener remains resident in memory and accepts
run-time commands to configure (or stop) the attention handler.

Signed-off-by: Ben Tyner <ben.tyner@ibm.com>
Change-Id: I032fe25a49d43d91a1f7d41cc34318839cd7de05
diff --git a/listener.cpp b/listener.cpp
new file mode 100644
index 0000000..c8ae642
--- /dev/null
+++ b/listener.cpp
@@ -0,0 +1,208 @@
+#include <libpdbg.h>
+
+#include <attn/attn_main.hpp>
+#include <boost/interprocess/ipc/message_queue.hpp>
+#include <cli.hpp>
+#include <listener.hpp>
+
+/** @brief openpower-hw-diags message queue name */
+static constexpr const char* mq_listener = "openpower-hw-diags-mq";
+
+/** @brief maximum length of command line parameter */
+static constexpr int max_command_len = 25;
+
+/** @brief end of command line args message */
+static const char* msg_send_end = "999999999999999";
+
+/** @brief structure for holding main args (for threads) */
+typedef struct
+{
+    int argc;
+    char** argv;
+} MainArgs_t;
+
+/**
+ * @brief Start a thread to monitor the attention GPIO
+ *
+ * @param i_config Attention handler configuration object
+ */
+void* threadGpioMon(void* i_config)
+{
+    // Configure and start attention monitor
+    attn::attnDaemon((attn::Config*)i_config);
+
+    pthread_exit(NULL);
+}
+
+/** @brief Start a thread to listen for attention handler messages */
+void* threadListener(void* i_params)
+{
+    using namespace boost::interprocess;
+
+    // convert thread params to main arguments
+    int argc    = static_cast<MainArgs_t*>(i_params)->argc;
+    char** argv = static_cast<MainArgs_t*>(i_params)->argv;
+
+    // vector to hold messages sent to listener
+    std::vector<std::string> messages;
+
+    // remove listener message queue if exists (does not throw)
+    message_queue::remove(mq_listener);
+
+    // thread handle for gpio monitor
+    pthread_t ptidGpio;
+
+    // status of gpio monitor
+    bool gpioMonEnabled = false;
+
+    // Parse command line args to see if any flags were passed, update the
+    // booleans accordingly and pass them to the config object constructor.
+    bool vital_enable     = true;
+    bool checkstop_enable = true;
+    bool ti_enable        = true;
+    bool bp_enable        = true;
+
+    // parse config options
+    parseConfig(argv, argv + argc, vital_enable, checkstop_enable, ti_enable,
+                bp_enable);
+
+    // create config
+    attn::Config config(vital_enable, checkstop_enable, ti_enable, bp_enable);
+
+    // initialize pdbg targets
+    pdbg_targets_init(nullptr);
+
+    // This is the main listener loop. All the above code will be executed
+    // only once. All other communtication with the attention handler will
+    // originate from here via the message queue.
+    do
+    {
+        // we will catch any exceptions from thread library
+        try
+        {
+            // create new message queue or open existing
+            message_queue mq(open_or_create, mq_listener, 1, max_command_len);
+
+            // message queue parameters
+            char buffer[max_command_len + 1];
+            size_t recvd_size;
+            unsigned int priority;
+
+            // We will continue receiving messages until we receive
+            // a msg_send_end message to indicate all command line parameters
+            // have been sent.
+            do
+            {
+                // wait for a message to arrive
+                mq.receive((void*)&buffer, max_command_len, recvd_size,
+                           priority);
+
+                // null terminate message and store
+                buffer[recvd_size] = '\0';
+                messages.push_back(buffer);
+
+            } while (buffer != std::string(msg_send_end));
+
+            messages.pop_back(); // remove msg_send_end message
+
+            // convert messages to command line arguments
+            std::vector<char*> argv;
+
+            for (const auto& arg : messages)
+            {
+                argv.push_back((char*)arg.data());
+            }
+
+            int argc = argv.size();
+            argv.push_back(nullptr);
+
+            // stop attention handler daemon?
+            if (true == getCliOption(argv.data(), argv.data() + argc, "--stop"))
+            {
+                message_queue::remove(mq_listener);
+                break;
+            }
+
+            // parse config options
+            parseConfig(argv.data(), argv.data() + argc, vital_enable,
+                        checkstop_enable, ti_enable, bp_enable);
+
+            // set config
+            config.setConfig(vital_enable, checkstop_enable, ti_enable,
+                             bp_enable);
+
+            // start attention handler daemon?
+            if (true ==
+                getCliOption(argv.data(), argv.data() + argc, "--start"))
+            {
+                if (false == gpioMonEnabled)
+                {
+                    if (0 == pthread_create(&ptidGpio, NULL, &threadGpioMon,
+                                            &config))
+                    {
+                        gpioMonEnabled = true;
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+            }
+        }
+
+        catch (interprocess_exception& e)
+        {
+            break;
+        }
+    } while (1);
+
+    // stop the gpio monitor if running
+    if (true == gpioMonEnabled)
+    {
+        pthread_cancel(ptidGpio);
+    }
+
+    pthread_exit(NULL);
+}
+
+/** @brief Send command line to a threadi */
+int sendCmdLine(int i_argc, char** i_argv)
+{
+    int count = 0; // number of arguments sent
+
+    using namespace boost::interprocess;
+
+    try
+    {
+        message_queue mq(open_only, mq_listener);
+
+        while (count < i_argc)
+        {
+            mq.send(i_argv[count], strlen(i_argv[count]), 0);
+            count++;
+        }
+        // indicate to listener last cmdline arg was sent
+        mq.send(msg_send_end, strlen(msg_send_end), 0);
+    }
+    catch (interprocess_exception& e)
+    {
+        count = 0; // assume no arguments sent
+    }
+    return count;
+}
+
+/** @brief See if the listener thread message queue exists */
+bool listenerMqExists()
+{
+    using namespace boost::interprocess;
+
+    try
+    {
+        message_queue mq(open_only, mq_listener);
+        return true;
+    }
+    catch (interprocess_exception& e)
+    {
+        return false;
+    }
+}