| #include "config.h" |
| #include <experimental/filesystem> |
| #include <cereal/archives/json.hpp> |
| #include <fstream> |
| #include <unistd.h> |
| #include "serialize.hpp" |
| #include <sdbusplus/server.hpp> |
| #include <phosphor-logging/log.hpp> |
| #include <chrono> |
| |
| namespace phosphor |
| { |
| namespace software |
| { |
| namespace updater |
| { |
| |
| namespace fs = std::experimental::filesystem; |
| |
| using namespace phosphor::logging; |
| using namespace std::chrono_literals; |
| |
| void storeToFile(std::string versionId, uint8_t priority) |
| { |
| auto bus = sdbusplus::bus::new_default(); |
| |
| if (!fs::is_directory(PERSIST_DIR)) |
| { |
| fs::create_directories(PERSIST_DIR); |
| } |
| std::string path = PERSIST_DIR + versionId; |
| |
| std::ofstream os(path.c_str()); |
| cereal::JSONOutputArchive oarchive(os); |
| oarchive(cereal::make_nvp("priority", priority)); |
| |
| std::string serviceFile = "obmc-flash-bmc-setenv@" + versionId + "\\x3d" + |
| std::to_string(priority) + ".service"; |
| auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, |
| SYSTEMD_INTERFACE, "StartUnit"); |
| method.append(serviceFile, "replace"); |
| bus.call_noreply(method); |
| |
| // On average it takes 1-2 seconds for the service to complete. |
| // Therefore timeout is set to 3x average completion time. |
| waitForServiceFile(serviceFile, 6); |
| } |
| |
| bool restoreFromFile(std::string versionId, uint8_t& priority) |
| { |
| std::string path = PERSIST_DIR + versionId; |
| if (fs::exists(path)) |
| { |
| std::ifstream is(path.c_str(), std::ios::in); |
| try |
| { |
| cereal::JSONInputArchive iarchive(is); |
| iarchive(cereal::make_nvp("priority", priority)); |
| return true; |
| } |
| catch (cereal::RapidJSONException& e) |
| { |
| fs::remove(path); |
| } |
| } |
| |
| // Find the mtd device "u-boot-env" to retrieve the environment variables |
| std::ifstream mtdDevices("/proc/mtd"); |
| std::string device, devicePath; |
| |
| try |
| { |
| while (std::getline(mtdDevices, device)) |
| { |
| if (device.find("u-boot-env") != std::string::npos) |
| { |
| devicePath = "/dev/" + device.substr(0, device.find(':')); |
| break; |
| } |
| } |
| |
| if (!devicePath.empty()) |
| { |
| std::ifstream input(devicePath.c_str()); |
| std::string envVars; |
| std::getline(input, envVars); |
| |
| std::string versionVar = versionId + "="; |
| auto varPosition = envVars.find(versionVar); |
| |
| if (varPosition != std::string::npos) |
| { |
| // Grab the environment variable for this versionId. These |
| // variables follow the format "versionId=priority\0" |
| auto var = envVars.substr(varPosition); |
| priority = std::stoi(var.substr(versionVar.length())); |
| return true; |
| } |
| } |
| } |
| catch (const std::exception& e) |
| { |
| } |
| |
| return false; |
| } |
| |
| void removeFile(std::string versionId) |
| { |
| std::string path = PERSIST_DIR + versionId; |
| if (fs::exists(path)) |
| { |
| fs::remove(path); |
| } |
| } |
| |
| void waitForServiceFile(const std::string& serviceFile, int timeout) |
| { |
| auto bus = sdbusplus::bus::new_default(); |
| |
| std::time_t start = time(0); |
| std::time_t end = time(0); |
| |
| while (end - start < timeout) |
| { |
| auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, |
| SYSTEMD_INTERFACE, "GetUnit"); |
| method.append(serviceFile); |
| auto result = bus.call(method); |
| |
| // "GetUnit" returns the unit object path for a unit name. |
| // If the unit is not found because it hasn't been loaded yet, |
| // or in this case because it has finished and exited, the call |
| // will fail. |
| if (result.is_method_error()) |
| { |
| return; |
| } |
| |
| std::this_thread::sleep_for(1s); |
| end = time(0); |
| } |
| |
| log<level::ERR>("Service file timed out!", |
| entry("FILENAME=%s", serviceFile.c_str())); |
| } |
| |
| } // namespace phosphor |
| } // namespace software |
| } // namespace openpower |