Add support for post code handling/eventing
Add support to consume a JSON configuration which
will let a platform configuration to dictate if
we need to do special handling or logging based
on specific post codes received.
Tested:
1. Created the following config and after restarting
`xyz.openbmc_project.State.Boot.PostCode@0.service`:
```
[
{
"primary": [1,2,5],
"event": {
"name": "xyz.openbmc_project.State.SMC.SMCFailed",
"arguments": {
"IDENTIFIER": "test",
"FAILURE_TYPE": "world"
}
}
}
]
```
Created the primary post code:
```
root@bmc:~# busctl set-property xyz.openbmc_project.State.Boot.Raw /xyz/openbmc_project/state/boot/raw0 xyz.openbmc_project.State.Boot.Raw Value '(ayay)' 3 0x1 0x2 0x5 0x0
```
Check that the log is created:
```
root@bmc:~# busctl introspect -l xyz.openbmc_project.Logging /xyz/openbmc_project/logging/entry/2 xyz.openbmc_project.Logging.Entry | grep "AdditionalData\|Message"
.AdditionalData property a{ss} 6 "FAILURE_TYPE" "world" "IDENTIFIER" "test" <snip>
.Message property s "xyz.openbmc_project.State.SMC.SMCFailed"
```
2. Replace the config with:
```
[
{
"primary": [1,2,6],
"targets": ["testservice.service"]
}
]
```
Create a test unit file:
testservice.service:
```
[Unit]
Description=A simple oneshot service
[Service]
Type=oneshot
ExecStart=/bin/bash -c "echo Hello world > /tmp/test.txt"
```
Issue a new post code with the new config.
```
systemctl daemon-reload
systemctl restart xyz.openbmc_project.State.Boot.PostCode@0.service
root@bmc:/usr/lib/systemd/system# busctl set-property xyz.openbmc_project.State.Boot.Raw /xyz/openbmc_project/state/boot/raw0 xyz.openbmc_project.State.Boot.Raw Value '(ayay)' 3 0x1 0x2 0x5 0x0
root@bmc:/usr/lib/systemd/system# cat /tmp/test.txt
Hello world
```
Change-Id: Ibaa09fb88190a344fd7c4fadd24dda4aa4ae0633
Signed-off-by: Amithash Prasad <amithash@meta.com>
diff --git a/src/main.cpp b/src/main.cpp
index be4b6b9..6ed2e55 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -23,19 +23,24 @@
int optIndex = 0;
int ret = 0;
int node = 0;
+ PostCodeHandlers handlers;
std::string intfName;
static struct option longOpts[] = {{"host", required_argument, 0, 'h'},
+ {"config", required_argument, 0, 'c'},
{0, 0, 0, 0}};
- while ((arg = getopt_long(argc, argv, "h:", longOpts, &optIndex)) != -1)
+ while ((arg = getopt_long(argc, argv, "h:c:", longOpts, &optIndex)) != -1)
{
switch (arg)
{
case 'h':
node = std::stoi(optarg);
break;
+ case 'c':
+ handlers.load(optarg);
+ break;
default:
break;
}
@@ -64,7 +69,7 @@
bus.request_name(intfName.c_str());
- PostCode postCode{bus, dbusObjName.c_str(), eventP, node};
+ PostCode postCode{bus, dbusObjName.c_str(), eventP, node, handlers};
try
{
diff --git a/src/post_code.cpp b/src/post_code.cpp
index 82791c2..edad39d 100644
--- a/src/post_code.cpp
+++ b/src/post_code.cpp
@@ -21,10 +21,112 @@
#include <cereal/types/map.hpp>
#include <cereal/types/tuple.hpp>
#include <cereal/types/vector.hpp>
+#include <phosphor-logging/commit.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/exception.hpp>
#include <iomanip>
+using nlohmann::json;
+
const static constexpr auto timeoutMicroSeconds = 1000000;
+/* systemd service to kick start a target. */
+constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
+constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1";
+constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
+
+void PostCodeEvent::raise() const
+{
+ json j = {{name, args}};
+ try
+ {
+ sdbusplus::exception::throw_via_json(j);
+ }
+ catch (sdbusplus::exception::generated_event_base& e)
+ {
+ auto path = lg2::commit(std::move(e));
+ std::cout << path.str << std::endl;
+ }
+}
+
+void from_json(const json& j, PostCodeEvent& event)
+{
+ j.at("name").get_to(event.name);
+ for (const auto& entry : j.at("arguments").items())
+ {
+ if (entry.value().is_string())
+ {
+ event.args[entry.key()] = entry.value().get<std::string>();
+ }
+ else if (entry.value().is_number_integer())
+ {
+ event.args[entry.key()] = entry.value().get<int>();
+ }
+ }
+}
+
+void from_json(const json& j, PostCodeHandler& handler)
+{
+ j.at("primary").get_to(handler.primary);
+ if (j.contains("secondary"))
+ {
+ secondarycode_t secondary;
+ j.at("secondary").get_to(secondary);
+ handler.secondary = secondary;
+ }
+ if (j.contains("targets"))
+ {
+ j.at("targets").get_to(handler.targets);
+ }
+ if (j.contains("event"))
+ {
+ PostCodeEvent event;
+ j.at("event").get_to(event);
+ handler.event = event;
+ }
+}
+
+const PostCodeHandler* PostCodeHandlers::find(postcode_t code)
+{
+ for (const auto& handler : handlers)
+ {
+ if (handler.primary == std::get<0>(code) &&
+ (!handler.secondary || *handler.secondary == std::get<1>(code)))
+ {
+ return &handler;
+ }
+ }
+ return nullptr;
+}
+
+void PostCodeHandlers::handle(postcode_t code)
+{
+ const PostCodeHandler* handler = find(code);
+ if (!handler)
+ {
+ return;
+ }
+ for (const auto& target : handler->targets)
+ {
+ auto bus = sdbusplus::bus::new_default();
+ auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
+ SYSTEMD_INTERFACE, "StartUnit");
+ method.append(target);
+ method.append("replace");
+ bus.call_noreply(method);
+ }
+ if (handler->event)
+ {
+ (*(handler->event)).raise();
+ }
+}
+
+void PostCodeHandlers::load(const std::string& path)
+{
+ std::ifstream ifs(path);
+ handlers = json::parse(ifs).template get<std::vector<PostCodeHandler>>();
+ ifs.close();
+}
void PostCode::deleteAll()
{
@@ -152,6 +254,7 @@
"REDFISH_MESSAGE_ARGS=%d,%s,%s", currentBootCycleIndex,
timeOffsetStr.str().c_str(), hexCode.str().c_str()));
#endif
+ postCodeHandlers.handle(code);
return;
}