Implement post code manager
This depends on interfaces definition:
https://gerrit.openbmc-project.xyz/c/openbmc/phosphor-dbus-interfaces/+/20474
Implement method and properties defined in PostCode.interface.yaml
under phosphor-dbus-interfaces/xyz/openbmc_project/State/Boot
1. Method: std::vector<uint64_t> PostCode::getPostCodes(uint16_t index)
2. Properties: CurrentBootCycleIndex/MaxBootCycleNum
Test-By:
Every cycle post codes is saved in "/var/lib/phosphor-post-code-manager"
"1" file is saved all post codes for cycle 1
"2" file is saved all post codes for cycle 2
"CurrentBootCycleIndex" file is saved the current boot cycle number.
root@wolfpass:/var/lib/phosphor-post-code-manager# ls
1 2 CurrentBootCycleIndex
Change-Id: Ia89b9121983261fef5573092d890beb84626ceeb
Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..dd27708
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,98 @@
+---
+Language: Cpp
+# BasedOnStyle: LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ AfterClass: true
+ AfterControlStatement: true
+ AfterEnum: true
+ AfterFunction: true
+ AfterNamespace: true
+ AfterObjCDeclaration: true
+ AfterStruct: true
+ AfterUnion: true
+ BeforeCatch: true
+ BeforeElse: true
+ IndentBraces: false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: AfterColon
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: true
+PointerAlignment: Left
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IncludeBlocks: Regroup
+IncludeCategories:
+ - Regex: '^[<"](gtest|gmock)'
+ Priority: 5
+ - Regex: '^"config.h"'
+ Priority: -1
+ - Regex: '^".*\.hpp"'
+ Priority: 1
+ - Regex: '^<.*\.h>'
+ Priority: 2
+ - Regex: '^<.*'
+ Priority: 3
+ - Regex: '.*'
+ Priority: 4
+IndentCaseLabels: true
+IndentWidth: 4
+IndentWrappedFunctionNames: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+ReflowComments: true
+SortIncludes: true
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp11
+TabWidth: 4
+UseTab: Never
+...
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..594d839
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,45 @@
+cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR)
+project(post-code-manager CXX)
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
+include(GNUInstallDirs)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc)
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+set(DBUS_OBJECT_NAME "xyz/openbmc_project/State/Boot/PostCode")
+set(DBUS_INTF_NAME "xyz.openbmc_project.State.Boot.PostCode")
+
+add_definitions(-DDBUS_OBJECT_NAME="/${DBUS_OBJECT_NAME}")
+add_definitions(-DDBUS_INTF_NAME="${DBUS_INTF_NAME}")
+set(SRC_FILES src/post_code.cpp
+ src/main.cpp )
+set ( SERVICE_FILES
+ service_files/xyz.openbmc_project.State.Boot.PostCode.service )
+
+# import sdbusplus
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(SDBUSPLUSPLUS sdbusplus REQUIRED)
+include_directories(${SDBUSPLUSPLUS_INCLUDE_DIRS})
+link_directories(${SDBUSPLUSPLUS_LIBRARY_DIRS})
+find_program(SDBUSPLUSPLUS sdbus++)
+
+# import phosphor-logging
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(LOGGING phosphor-logging REQUIRED)
+include_directories(${LOGGING_INCLUDE_DIRS})
+link_directories(${LOGGING_LIBRARY_DIRS})
+
+# phosphor-dbus-interfaces
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(DBUSINTERFACE phosphor-dbus-interfaces REQUIRED)
+include_directories(${DBUSINTERFACE_INCLUDE_DIRS})
+link_directories(${DBUSINTERFACE_LIBRARY_DIRS})
+
+add_executable(${PROJECT_NAME} ${SRC_FILES})
+target_link_libraries(${PROJECT_NAME} ${DBUSINTERFACE_LIBRARIES} )
+target_link_libraries(${PROJECT_NAME} "${SDBUSPLUSPLUS_LIBRARIES} -lstdc++fs -lphosphor_dbus")
+
+install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
+install (FILES ${SERVICE_FILES} DESTINATION /lib/systemd/system/)
\ No newline at end of file
diff --git a/MAINTAINERS b/MAINTAINERS
new file mode 100644
index 0000000..de6cc54
--- /dev/null
+++ b/MAINTAINERS
@@ -0,0 +1,45 @@
+How to use this list:
+ Find the most specific section entry (described below) that matches where
+ your change lives and add the reviewers (R) and maintainers (M) as
+ reviewers. You can use the same method to track down who knows a particular
+ code base best.
+
+ Your change/query may span multiple entries; that is okay.
+
+ If you do not find an entry that describes your request at all, someone
+ forgot to update this list; please at least file an issue or send an email
+ to a maintainer, but preferably you should just update this document.
+
+Description of section entries:
+
+ Section entries are structured according to the following scheme:
+
+ X: NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>
+ X: ...
+ .
+ .
+ .
+
+ Where REPO_NAME is the name of the repository within the OpenBMC GitHub
+ organization; FILE_PATH is a file path within the repository, possibly with
+ wildcards; X is a tag of one of the following types:
+
+ M: Denotes maintainer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>;
+ if omitted from an entry, assume one of the maintainers from the
+ MAINTAINERS entry.
+ R: Denotes reviewer; has fields NAME <EMAIL_USERNAME@DOMAIN> <IRC_USERNAME!>;
+ these people are to be added as reviewers for a change matching the repo
+ path.
+ F: Denotes forked from an external repository; has fields URL.
+
+ Line comments are to be denoted "# SOME COMMENT" (typical shell style
+ comment); it is important to follow the correct syntax and semantics as we
+ may want to use automated tools with this file in the future.
+
+ A change cannot be added to an OpenBMC repository without a MAINTAINER's
+ approval; thus, a MAINTAINER should always be listed as a reviewer.
+
+START OF MAINTAINERS LIST
+-------------------------
+
+M: Kuiying Wang <kuiying.wang@intel.com> <kuiyingw>
\ No newline at end of file
diff --git a/inc/post_code.hpp b/inc/post_code.hpp
new file mode 100644
index 0000000..3c4ac4a
--- /dev/null
+++ b/inc/post_code.hpp
@@ -0,0 +1,161 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#pragma once
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <cereal/access.hpp>
+#include <cereal/archives/json.hpp>
+#include <cereal/cereal.hpp>
+#include <cereal/types/vector.hpp>
+#include <experimental/filesystem>
+#include <fstream>
+#include <iostream>
+#include <phosphor-logging/elog-errors.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+#include <xyz/openbmc_project/State/Boot/PostCode/server.hpp>
+#include <xyz/openbmc_project/State/Host/server.hpp>
+
+#define MaxPostCodeCycles 100
+
+const static constexpr char *PostCodePath =
+ "/xyz/openbmc_project/state/boot/raw";
+const static constexpr char *PropertiesIntf = "org.freedesktop.DBus.Properties";
+const static constexpr char *PostCodeListPath =
+ "/var/lib/phosphor-post-code-manager/";
+const static constexpr char *CurrentBootCycleIndexName =
+ "CurrentBootCycleIndex";
+const static constexpr char *HostStatePath = "/xyz/openbmc_project/state/host0";
+
+struct EventDeleter
+{
+ void operator()(sd_event *event) const
+ {
+ event = sd_event_unref(event);
+ }
+};
+using EventPtr = std::unique_ptr<sd_event, EventDeleter>;
+namespace fs = std::experimental::filesystem;
+namespace StateServer = sdbusplus::xyz::openbmc_project::State::server;
+
+using post_code =
+ sdbusplus::xyz::openbmc_project::State::Boot::server::PostCode;
+
+struct PostCode : sdbusplus::server::object_t<post_code>
+{
+ PostCode(sdbusplus::bus::bus &bus, const char *path, EventPtr &event) :
+ sdbusplus::server::object_t<post_code>(bus, path), bus(bus),
+ propertiesChangedSignalRaw(
+ bus,
+ sdbusplus::bus::match::rules::type::signal() +
+ sdbusplus::bus::match::rules::member("PropertiesChanged") +
+ sdbusplus::bus::match::rules::path(PostCodePath) +
+ sdbusplus::bus::match::rules::interface(PropertiesIntf),
+ [this](sdbusplus::message::message &msg) {
+ std::string objectName;
+ std::map<std::string, sdbusplus::message::variant<uint64_t>>
+ msgData;
+ msg.read(objectName, msgData);
+ // Check if it was the Value property that changed.
+ auto valPropMap = msgData.find("Value");
+ {
+ if (valPropMap != msgData.end())
+ {
+ this->savePostCodes(
+ sdbusplus::message::variant_ns::get<uint64_t>(
+ valPropMap->second));
+ }
+ }
+ }),
+ propertiesChangedSignalCurrentHostState(
+ bus,
+ sdbusplus::bus::match::rules::type::signal() +
+ sdbusplus::bus::match::rules::member("PropertiesChanged") +
+ sdbusplus::bus::match::rules::path(HostStatePath) +
+ sdbusplus::bus::match::rules::interface(PropertiesIntf),
+ [this](sdbusplus::message::message &msg) {
+ std::string objectName;
+ std::map<std::string, sdbusplus::message::variant<std::string>>
+ msgData;
+ msg.read(objectName, msgData);
+ // Check if it was the Value property that changed.
+ auto valPropMap = msgData.find("CurrentHostState");
+ {
+ if (valPropMap != msgData.end())
+ {
+ StateServer::Host::HostState currentHostState =
+ StateServer::Host::convertHostStateFromString(
+ sdbusplus::message::variant_ns::get<
+ std::string>(valPropMap->second));
+ if (currentHostState ==
+ StateServer::Host::HostState::Off)
+ {
+ if (this->currentBootCycleIndex() >=
+ this->maxBootCycleNum())
+ {
+ this->currentBootCycleIndex(1);
+ }
+ else
+ {
+ this->currentBootCycleIndex(
+ this->currentBootCycleIndex() + 1);
+ }
+ this->postCodes.clear();
+ }
+ }
+ }
+ })
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "PostCode is created");
+ auto dir = fs::path(PostCodeListPath);
+ fs::create_directories(dir);
+ strPostCodeListPath = PostCodeListPath;
+ strCurrentBootCycleIndexName = CurrentBootCycleIndexName;
+ uint16_t index = 0;
+ deserialize(
+ fs::path(strPostCodeListPath + strCurrentBootCycleIndexName),
+ index);
+ currentBootCycleIndex(index);
+ maxBootCycleNum(MaxPostCodeCycles);
+ if (currentBootCycleIndex() >= maxBootCycleNum())
+ {
+ currentBootCycleIndex(1);
+ }
+ else
+ {
+ currentBootCycleIndex(currentBootCycleIndex() + 1);
+ }
+ }
+ ~PostCode()
+ {
+ }
+
+ std::vector<uint64_t> getPostCodes(uint16_t index) override;
+
+ private:
+ sdbusplus::bus::bus &bus;
+ std::vector<uint64_t> postCodes;
+ std::string strPostCodeListPath;
+ std::string strCurrentBootCycleIndexName;
+ void savePostCodes(uint64_t code);
+ sdbusplus::bus::match_t propertiesChangedSignalRaw;
+ sdbusplus::bus::match_t propertiesChangedSignalCurrentHostState;
+ fs::path serialize(const std::string &path);
+ bool deserialize(const fs::path &path, uint16_t &index);
+ bool deserializePostCodes(const fs::path &path,
+ std::vector<uint64_t> &codes);
+};
diff --git a/service_files/xyz.openbmc_project.State.Boot.PostCode.service b/service_files/xyz.openbmc_project.State.Boot.PostCode.service
new file mode 100644
index 0000000..67bc43f
--- /dev/null
+++ b/service_files/xyz.openbmc_project.State.Boot.PostCode.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Post code manager
+
+[Service]
+ExecStart=/usr/bin/env post-code-manager
+SyslogIdentifier=post-code-manager
+Type=dbus
+BusName=xyz.openbmc_project.State.Boot.PostCode
+
+[Install]
+WantedBy=obmc-standby.target
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..fcd09d3
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,60 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#include "post_code.hpp"
+
+int main(int argc, char* argv[])
+{
+ int ret = 0;
+
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Start post code manager service...");
+
+ sd_event* event = nullptr;
+ ret = sd_event_default(&event);
+ if (ret < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error creating a default sd_event handler");
+ return ret;
+ }
+ EventPtr eventP{event};
+ event = nullptr;
+
+ sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
+ sdbusplus::server::manager_t m{bus, DBUS_OBJECT_NAME};
+
+ bus.request_name(DBUS_INTF_NAME);
+
+ PostCode postCode{bus, DBUS_OBJECT_NAME, eventP};
+
+ try
+ {
+ bus.attach_event(eventP.get(), SD_EVENT_PRIORITY_NORMAL);
+ ret = sd_event_loop(eventP.get());
+ if (ret < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error occurred during the sd_event_loop",
+ phosphor::logging::entry("RET=%d", ret));
+ }
+ }
+ catch (std::exception& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
+ return -1;
+ }
+ return 0;
+}
diff --git a/src/post_code.cpp b/src/post_code.cpp
new file mode 100644
index 0000000..8a94911
--- /dev/null
+++ b/src/post_code.cpp
@@ -0,0 +1,114 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#include "post_code.hpp"
+std::vector<uint64_t> PostCode::getPostCodes(uint16_t index)
+{
+ std::vector<uint64_t> codes;
+
+ if (currentBootCycleIndex() == index)
+ return postCodes;
+ deserializePostCodes(fs::path(strPostCodeListPath + std::to_string(index)),
+ codes);
+ return codes;
+}
+void PostCode::savePostCodes(uint64_t code)
+{
+ postCodes.push_back(code);
+ serialize(fs::path(PostCodeListPath));
+ return;
+}
+
+fs::path PostCode::serialize(const std::string& path)
+{
+ try
+ {
+ uint16_t index = currentBootCycleIndex();
+ fs::path fullPath(path + strCurrentBootCycleIndexName);
+ std::ofstream os(fullPath.c_str(), std::ios::binary);
+ cereal::JSONOutputArchive oarchive(os);
+ oarchive(index);
+
+ std::ofstream osPostCodes(
+ (path + std::to_string(currentBootCycleIndex())).c_str(),
+ std::ios::binary);
+ cereal::JSONOutputArchive oarchivePostCodes(osPostCodes);
+ oarchivePostCodes(postCodes);
+ }
+ catch (cereal::Exception& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
+ return "";
+ }
+ catch (const fs::filesystem_error& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
+ return "";
+ }
+ return path;
+}
+
+bool PostCode::deserialize(const fs::path& path, uint16_t& index)
+{
+ try
+ {
+ if (fs::exists(path))
+ {
+ std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
+ cereal::JSONInputArchive iarchive(is);
+ iarchive(index);
+ return true;
+ }
+ return false;
+ }
+ catch (cereal::Exception& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
+ return false;
+ }
+ catch (const fs::filesystem_error& e)
+ {
+ return false;
+ }
+
+ return false;
+}
+
+bool PostCode::deserializePostCodes(const fs::path& path,
+ std::vector<uint64_t>& codes)
+{
+ try
+ {
+ if (fs::exists(path))
+ {
+ std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
+ cereal::JSONInputArchive iarchive(is);
+ iarchive(codes);
+ return true;
+ }
+ return false;
+ }
+ catch (cereal::Exception& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
+ return false;
+ }
+ catch (const fs::filesystem_error& e)
+ {
+ return false;
+ }
+
+ return false;
+}