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;
+    }
+}
diff --git a/listener.hpp b/listener.hpp
new file mode 100644
index 0000000..48f5cbf
--- /dev/null
+++ b/listener.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+/**
+ * @brief Start a thread to listen for attention handler messages
+ *
+ * @param i_params command line arguments passed to main
+ */
+void* threadListener(void* i_params);
+
+/**
+ * @brief Send command line to a thread
+ *
+ * @param i_argc command line arguments count
+ * @param i_argv command line arguments
+ *
+ * @return number of cmd line arguments sent
+ */
+int sendCmdLine(int i_argc, char** i_argv);
+
+/**
+ * @brief See if the listener thread message queue exists
+ *
+ *  @return true if message queue exists, else false
+ */
+bool listenerMqExists();
diff --git a/main.cpp b/main.cpp
index 4e73e03..ad40325 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,8 +1,7 @@
-#include <libpdbg.h>
-
 #include <analyzer/analyzer_main.hpp>
-#include <attn/attn_main.hpp>
+#include <boost/interprocess/ipc/message_queue.hpp>
 #include <cli.hpp>
+#include <listener.hpp>
 
 /**
  * @brief Attention handler application main()
@@ -12,61 +11,110 @@
  * gpio or it will be loaded as an application to analyze hardware and
  * diagnose hadrware error conditions.
  *
- * Command line arguments:
+ *     Usage:
+ *        --analyze:              Analyze the hardware
+ *        --start:                Start the attention handler
+ *        --stop:                 Stop the attention handler
+ *        --all <on|off>:         All attention handling
+ *        --vital <on|off>:       Vital attention handling
+ *        --checkstop <on|off>:   Checkstop attention handling
+ *        --terminate <on|off>:   Terminate Immiediately attention handling
+ *        --breakpoints <on|off>: Breakpoint attention handling
  *
- * commands:
- *
- *   analyze            analyze hardware
- *
- * options:
- *
- *  --daemon            load application as a daemon
- *  --vital off         disable vital attention handling (daemon mode)
- *  --checkstop off     disable checkstop attention handling (daemon mode)
- *  --terminate off     disable TI attention handling (daemon mode)
- *  --breakpoints off   disable breakpoint attention handling (daemon mode)
- *
- *  example:
- *
- *    openpower-hw-diags --daemon --terminate off
+ *     Example: openpower-hw-diags --start --vital off
  *
  * @return 0 = success
  */
 int main(int argc, char* argv[])
 {
-    int rc = 0; // return code
+    int rc = 0; // assume success
 
-    // attention handler configuration flags
-    bool vital_enable     = true;
-    bool checkstop_enable = true;
-    bool ti_enable        = true;
-    bool bp_enable        = true;
+    using namespace boost::interprocess;
 
-    // initialize pdbg targets
-    pdbg_targets_init(nullptr);
-
-    // get configuration options
-    parseConfig(argv, argv + argc, vital_enable, checkstop_enable, ti_enable,
-                bp_enable);
-
-    // check if we are being loaded as a daemon
-    if (true == getCliOption(argv, argv + argc, "--daemon"))
+    if (argc == 1)
     {
-        attn::Config config(vital_enable, checkstop_enable, ti_enable,
-                            bp_enable);
-
-        // Configure and start attention monitor
-        attn::attnDaemon(&config);
+        printf("openpower-hw-diags <options>\n");
+        printf("options:\n");
+        printf("  --analyze:              Analyze the hardware\n");
+        printf("  --start:                Start the attention handler\n");
+        printf("  --stop:                 Stop the attention handler\n");
+        printf("  --all <on|off>:         All attention handling\n");
+        printf("  --vital <on|off>:       Vital attention handling\n");
+        printf("  --checkstop <on|off>:   Checkstop attention handling\n");
+        printf("  --terminate <on|off>:   Terminate Immediately attention "
+               "handling\n");
+        printf("  --breakpoints <on|off>: Breakpoint attention handling\n");
     }
-    // we are being loaded as an application
     else
     {
-        // Request to analyze the hardware for error conditions
-        if (true == getCliOption(argv, argv + argc, "analyze"))
+        // todo usage
+
+        // Either analyze (application mode) or daemon mode
+        if (true == getCliOption(argv, argv + argc, "--analyze"))
         {
             analyzer::analyzeHardware();
         }
-    }
+        // daemon mode
+        else
+        {
+            // assume listener is not running
+            bool listenerStarted = false;
+            bool newListener     = false;
 
+            pthread_t ptidListener; // handle to listener thread
+
+            // see if listener is already started
+            listenerStarted = listenerMqExists();
+
+            // listener is not running so start it
+            if (false == listenerStarted)
+            {
+                // create listener thread
+                if (0 ==
+                    pthread_create(&ptidListener, NULL, &threadListener, NULL))
+                {
+                    listenerStarted = true;
+                    newListener     = true;
+                }
+                else
+                {
+                    rc = 1;
+                }
+            }
+
+            // listener was running or just started
+            if (true == listenerStarted)
+            {
+                // If we created a new listener this instance of
+                // openpower-hw-diags will become our daemon (it will not exit
+                // until stopped).
+                if (true == newListener)
+                {
+                    bool listenerReady = false;
+
+                    // It may take some time for the listener to become ready,
+                    // we will wait until the message queue has been created
+                    // before starting to communicate with our daemon.
+                    while (false == listenerReady)
+                    {
+                        usleep(500);
+                        listenerReady = listenerMqExists();
+                    }
+                }
+
+                // send cmd line to listener thread
+                if (argc != sendCmdLine(argc, argv))
+                {
+                    rc = 1;
+                }
+
+                // if this is a new listener let it run until "stopped"
+                if (true == newListener)
+                {
+                    pthread_join(ptidListener, NULL);
+                }
+            }
+        }
+    }
     return rc;
 }
diff --git a/meson.build b/meson.build
index 9edebbf..ef2b206 100644
--- a/meson.build
+++ b/meson.build
@@ -15,7 +15,11 @@
 subdir('analyzer')
 subdir('attn')
 
-executable('openpower-hw-diags', 'main.cpp', 'cli.cpp',
+pthread = declare_dependency(link_args : '-pthread')
+lrt = declare_dependency(link_args : '-lrt')
+
+executable('openpower-hw-diags', 'main.cpp', 'cli.cpp', 'listener.cpp',
+            dependencies : [lrt, pthread],
             link_with : [analyzer, attn],
             install : true)