#pragma once #include #include #include #include #include #include #include #include #include #include namespace rublon { struct closefile_deleter { void operator()(FILE * f) const { pclose(f); } }; std::string exec(const char * cmd) { std::array< char, 128 > buffer; std::string result; std::unique_ptr< FILE, closefile_deleter > pipe(popen(cmd, "r")); if(!pipe) { return ""; } while(fgets(buffer.data(), static_cast< int >(buffer.size()), pipe.get()) != nullptr) { result += buffer.data(); } return result; } std::map< std::string, std::string > getSSHDConfig() { std::istringstream iss(exec("sshd -T")); std::map< std::string, std::string > result; for(std::string line; std::getline(iss, line);) { auto first_token = line.substr(0, line.find(' ')); result[first_token] = line.substr(line.find(' ') + 1); } return result; } class Status { std::string_view _statusDirPath = "/var/lib/rublon"; std::string_view _statusFilePath = "/var/lib/rublon/install.json"; RapidJSONPMRStackAlloc< 4 * 1024 > _alloc; Document _data; bool _statusUpdated; std::string_view _appVersionKey = "/appVer"; std::string_view _appTypeKey = "/type"; std::string_view _paramSystemName = "/params/os"; std::string_view _paramSSHDBase = "/params/sshd_config/"; public: Status() : _alloc{}, _data{&_alloc}, _statusUpdated{false} { if(not details::exists(_statusFilePath.data())) { log(LogLevel::Info, "application first run, creating status file at %s", _statusFilePath.data()); details::mkdir(_statusDirPath.data()); details::touch(_statusFilePath.data()); } std::ifstream ifs{_statusFilePath.data()}; if(!ifs.is_open()) { return; } rapidjson::IStreamWrapper isw{ifs}; _data.ParseStream(isw); } void updateAppVersion(std::string_view newVersion) { RapidJSONPMRStackAlloc< 512 > stackAlloc; auto jsonPointer = JSONPointer{_appVersionKey.data(), &stackAlloc}; auto version = jsonPointer.Get(_data); if(not version || version->GetString() != newVersion) { markUpdated(); jsonPointer.Set(_data, Value{newVersion.data(), _data.GetAllocator()}); } } void updateSystemVersion(std::string_view system) { RapidJSONPMRStackAlloc< 512 > stackAlloc; auto jsonPointer = JSONPointer{_paramSystemName.data(), &stackAlloc}; auto version = jsonPointer.Get(_data); if(not version || version->GetString() != system) { markUpdated(); jsonPointer.Set(_data, Value{system.data(), _data.GetAllocator()}); } } void updateSSHDConfig() { using namespace std::string_view_literals; constexpr auto keys = make_array("authenticationmethods"sv, "challengeresponseauthentication"sv, "kbdinteractiveauthentication"sv, "logingracetime"sv, "maxauthtries"sv, "passwordauthentication"sv, "permitemptypasswords"sv, "permitrootlogin"sv, "pubkeyauthentication"sv, "usepam"sv); auto config = getSSHDConfig(); for(const auto key : keys) { auto [currentPair, inserted] = config.try_emplace(std::string{key.data()}, "N/A"); auto & [currentKey, currentValue] = *currentPair; const auto jsonPath = std::string{_paramSSHDBase.data()} + key.data(); RapidJSONPMRStackAlloc< 512 > stackAlloc; auto jsonPointer = JSONPointer{jsonPath.c_str(), &stackAlloc}; auto oldValue = jsonPointer.Get(_data); if(not oldValue || oldValue->GetString() != currentValue) { _statusUpdated = true; jsonPointer.Set(_data, Value{currentValue.c_str(), _data.GetAllocator()}); } } } void markUpdated() { _statusUpdated = true; } bool updated() const { return _statusUpdated; } void save() { if(updated()) { memory::Monotonic_8k_Resource tmpResource; RapidJSONPMRAlloc alloc{&tmpResource}; FileWriter s{_statusFilePath}; rapidjson::PrettyWriter< FileWriter, rapidjson::UTF8<>, rapidjson::UTF8<>, RapidJSONPMRAlloc > writer{s, &alloc}; writer.SetIndent(' ', 2); _data.Accept(writer); } } std::string print() { std::string result; memory::Monotonic_8k_Resource tmpResource; RapidJSONPMRAlloc alloc{&tmpResource}; StringWriter s{result}; rapidjson::PrettyWriter< StringWriter< std::string >, rapidjson::UTF8<>, rapidjson::UTF8<>, RapidJSONPMRAlloc > writer{s, &alloc}; writer.SetIndent(' ', 2); _data.Accept(writer); return result; } Document & data() { return _data; } }; class CheckApplication { tl::expected< bool, Error > persistStatus(Status & status) const { status.data().RemoveMember("systemToken"); status.save(); return true; } public: tl::expected< int, Error > call(const CoreHandler_t & coreHandler, std::string_view systemToken) const { memory::MonotonicStack_1k_Resource mr; RapidJSONPMRAlloc alloc{&mr}; constexpr std::string_view api = "/api/app/init"; Status status; const auto persist = [&](const auto /*ok*/) { return this->persistStatus(status); }; status.updateAppVersion(RUBLON_VERSION_STRING); status.updateSystemVersion(details::osName(&mr)); status.updateSSHDConfig(); if(status.updated()) { auto & alloc = status.data().GetAllocator(); status.data().AddMember("systemToken", Value{systemToken.data(), alloc}, alloc); return coreHandler.request(alloc, api, status.data()).and_then(persist); } return 0; } }; } // namespace rublon