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)