#include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "version.hpp" #include "watch.hpp" #include "image_manager.hpp" namespace phosphor { namespace software { namespace manager { using namespace phosphor::logging; using namespace sdbusplus::xyz::openbmc_project::Software::Version::Error; namespace fs = std::experimental::filesystem; struct RemovablePath { fs::path path; RemovablePath(const fs::path& path) : path(path) {} ~RemovablePath() { fs::remove_all(path); } }; int Manager::processImage(const std::string& tarFilePath) { if (!fs::is_regular_file(tarFilePath)) { log("Error tarball does not exist", entry("FILENAME=%s", tarFilePath)); report(xyz::openbmc_project::Software::Version:: ManifestFileFailure::PATH( tarFilePath.c_str())); return -1; } RemovablePath tarPathRemove(tarFilePath); fs::path tmpDirPath(std::string{IMG_UPLOAD_DIR}); tmpDirPath /= "imageXXXXXX"; // Need tmp dir to write MANIFEST file to. if (!mkdtemp(const_cast(tmpDirPath.c_str()))) { log("Error occured during mkdtemp", entry("ERRNO=%d", errno)); report(xyz::openbmc_project::Software::Version:: InternalFailure::FAIL("mkdtemp")); return -1; } RemovablePath tmpDirRemove(tmpDirPath); fs::path manifestPath = tmpDirPath; manifestPath /= MANIFEST_FILE_NAME; int status = 0; pid_t pid = fork(); // Get the MANIFEST FILE if (pid == 0) { // child process execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), MANIFEST_FILE_NAME, "-C", tmpDirPath.c_str(), (char*)0); // execl only returns on fail log("Failed to untar file", entry("FILENAME=%s", tarFilePath)); report(xyz::openbmc_project::Software::Version:: ManifestFileFailure::PATH( manifestPath.c_str())); return -1; } else if (pid > 0) { waitpid(pid, &status, 0); } else { log("fork() failed."); report(xyz::openbmc_project::Software::Version:: InternalFailure::FAIL("fork")); return -1; } // Verify the manifest file if (!fs::is_regular_file(manifestPath)) { log("Error No manifest file"); report(xyz::openbmc_project::Software::Version:: ManifestFileFailure::PATH( manifestPath.c_str())); return -1; } // Get version auto version = Version::getValue(manifestPath.string(), "version"); if (version.empty()) { log("Error unable to read version from manifest file"); report(xyz::openbmc_project::Software::Version:: ManifestFileFailure::PATH( manifestPath.c_str())); return -1; } // Get purpose auto purposeString = Version::getValue(manifestPath.string(), "purpose"); if (purposeString.empty()) { log("Error unable to read purpose from manifest file"); report(xyz::openbmc_project::Software::Version:: ManifestFileFailure::PATH( manifestPath.c_str())); return -1; } auto purpose = Version::VersionPurpose::Unknown; try { purpose = Version::convertVersionPurposeFromString(purposeString); } catch (const sdbusplus::exception::InvalidEnumString& e) { log("Error: Failed to convert manifest purpose to enum. Setting to Unknown."); } // Compute id auto id = Version::getId(version); fs::path imageDirPath = std::string{IMG_UPLOAD_DIR}; imageDirPath /= id; if (fs::exists(imageDirPath)) { fs::remove_all(imageDirPath); } if (mkdir(imageDirPath.c_str(), S_IRWXU) != 0) { log("Error occured during mkdir", entry("ERRNO=%d", errno)); report(xyz::openbmc_project::Software::Version:: InternalFailure::FAIL("mkdir")); return -1; } // Untar tarball auto rc = unTar(tarFilePath, imageDirPath.string()); if (rc < 0) { log("Error occured during untar"); return -1; } // Create Version object auto objPath = std::string{SOFTWARE_OBJPATH} + '/' + id; this->versions.insert(std::make_pair( id, std::make_unique( this->bus, objPath, version, purpose, imageDirPath.string()))); return 0; } void Manager::erase(std::string entryId) { auto it = versions.find(entryId); if (it == versions.end()) { return; } if (it->second->isFunctional()) { log(("Error: Version " + entryId + \ " is currently running on the BMC." \ " Unable to remove.").c_str()); return; } // Delete image dir fs::path imageDirPath = (*(it->second)).path(); if (fs::exists(imageDirPath)) { fs::remove_all(imageDirPath); } this->versions.erase(entryId); } void Manager::removeVersion(sdbusplus::message::message& msg) { namespace mesg = sdbusplus::message; mesg::object_path objPath; msg.read(objPath); std::string path(std::move(objPath)); // Version id is the last item in the path auto pos = path.rfind("/"); if (pos == std::string::npos) { log("No version id found in object path", entry("OBJPATH=%s", path)); return; } auto versionId = path.substr(pos + 1); erase(versionId); } int Manager::unTar(const std::string& tarFilePath, const std::string& extractDirPath) { if (tarFilePath.empty()) { log("Error TarFilePath is empty"); report(xyz::openbmc_project::Software::Version:: UnTarFailure::PATH(tarFilePath.c_str())); return -1; } if (extractDirPath.empty()) { log("Error ExtractDirPath is empty"); report(xyz::openbmc_project::Software::Version:: UnTarFailure::PATH(tarFilePath.c_str())); return -1; } log("Untaring", entry("FILENAME=%s", tarFilePath), entry("EXTRACTIONDIR=%s", extractDirPath)); int status = 0; pid_t pid = fork(); if (pid == 0) { // child process execl("/bin/tar", "tar", "-xf", tarFilePath.c_str(), "-C", extractDirPath.c_str(), (char*)0); // execl only returns on fail log("Failed to untar file", entry("FILENAME=%s", tarFilePath)); report(xyz::openbmc_project::Software::Version:: UnTarFailure::PATH(tarFilePath.c_str())); return -1; } else if (pid > 0) { waitpid(pid, &status, 0); } else { log("fork() failed."); report(xyz::openbmc_project::Software::Version:: UnTarFailure::PATH(tarFilePath.c_str())); return -1; } return 0; } } // namespace manager } // namespace software } // namepsace phosphor