1 #include "ostreemanager.h"
9 #include <ostree-async-progress.h>
10 #include <boost/algorithm/string.hpp>
11 #include <boost/algorithm/string/classification.hpp>
12 #include <boost/algorithm/string/split.hpp>
13 #include <boost/filesystem.hpp>
16 #include "libaktualizr/packagemanagerfactory.h"
18 #include "bootloader/bootloader.h"
19 #include "logging/logging.h"
20 #include "storage/invstorage.h"
21 #include "utilities/utils.h"
23 AUTO_REGISTER_PACKAGE_MANAGER(PACKAGE_MANAGER_OSTREE,
OstreeManager);
25 static void aktualizr_progress_cb(OstreeAsyncProgress *progress, gpointer
data) {
27 if (mt->token !=
nullptr && !mt->token->canContinue()) {
28 g_cancellable_cancel(mt->cancellable.get());
31 g_autofree
char *status = ostree_async_progress_get_status(progress);
32 guint scanning = ostree_async_progress_get_uint(progress,
"scanning");
33 guint outstanding_fetches = ostree_async_progress_get_uint(progress,
"outstanding-fetches");
34 guint outstanding_metadata_fetches = ostree_async_progress_get_uint(progress,
"outstanding-metadata-fetches");
35 guint outstanding_writes = ostree_async_progress_get_uint(progress,
"outstanding-writes");
36 guint n_scanned_metadata = ostree_async_progress_get_uint(progress,
"scanned-metadata");
38 if (status !=
nullptr && *status !=
'\0') {
39 LOG_INFO <<
"ostree-pull: " << status;
40 }
else if (outstanding_fetches != 0) {
41 guint fetched = ostree_async_progress_get_uint(progress,
"fetched");
42 guint metadata_fetched = ostree_async_progress_get_uint(progress,
"metadata-fetched");
43 guint requested = ostree_async_progress_get_uint(progress,
"requested");
44 if (scanning != 0 || outstanding_metadata_fetches != 0) {
45 LOG_INFO <<
"ostree-pull: Receiving metadata objects: " << metadata_fetched
46 <<
" outstanding: " << outstanding_metadata_fetches;
47 if (mt->progress_cb) {
48 mt->progress_cb(mt->target,
"Receiving metadata objects", 0);
51 guint calculated = (fetched * 100) / requested;
52 if (calculated != mt->percent_complete) {
53 mt->percent_complete = calculated;
54 LOG_INFO <<
"ostree-pull: Receiving objects: " << calculated <<
"% ";
55 if (mt->progress_cb) {
56 mt->progress_cb(mt->target,
"Receiving objects", calculated);
60 }
else if (outstanding_writes != 0) {
61 LOG_INFO <<
"ostree-pull: Writing objects: " << outstanding_writes;
63 LOG_INFO <<
"ostree-pull: Scanning metadata: " << n_scanned_metadata;
64 if (mt->progress_cb) {
65 mt->progress_cb(mt->target,
"Scanning metadata", 0);
71 const std::string &ostree_server,
const KeyManager &keys,
73 OstreeProgressCb progress_cb) {
74 const std::string refhash = target.sha256Hash();
76 const char *
const commit_ids[] = {refhash.c_str()};
77 GError *error =
nullptr;
78 GVariantBuilder builder;
80 GObjectUniquePtr<OstreeAsyncProgress> progress =
nullptr;
82 GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(sysroot_path);
83 GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);
84 if (error !=
nullptr) {
85 LOG_ERROR <<
"Could not get OSTree repo";
90 GHashTable *ref_list =
nullptr;
91 if (ostree_repo_list_commit_objects_starting_with(repo.get(), refhash.c_str(), &ref_list,
nullptr, &error) != 0) {
92 guint length = g_hash_table_size(ref_list);
93 g_hash_table_destroy(ref_list);
96 LOG_DEBUG <<
"refhash already pulled";
100 if (error !=
nullptr) {
105 if (!OstreeManager::addRemote(repo.get(), ostree_server, keys)) {
109 g_variant_builder_init(&builder, G_VARIANT_TYPE(
"a{sv}"));
110 g_variant_builder_add(&builder,
"{s@v}",
"flags", g_variant_new_variant(g_variant_new_int32(0)));
112 g_variant_builder_add(&builder,
"{s@v}",
"refs", g_variant_new_variant(g_variant_new_strv(commit_ids, 1)));
114 options = g_variant_builder_end(&builder);
116 PullMetaStruct mt(target, token, g_cancellable_new(), std::move(progress_cb));
117 progress.reset(ostree_async_progress_new_and_connect(aktualizr_progress_cb, &mt));
118 if (ostree_repo_pull_with_options(repo.get(), remote, options, progress.get(), mt.cancellable.get(), &error) == 0) {
119 LOG_ERROR <<
"Error while pulling image: " << error->code <<
" " << error->message;
122 g_variant_unref(options);
125 ostree_async_progress_finish(progress.get());
126 g_variant_unref(options);
131 const char *opt_osname =
nullptr;
132 GCancellable *cancellable =
nullptr;
133 GError *error =
nullptr;
134 g_autofree
char *revision =
nullptr;
136 if (!config.os.empty()) {
137 opt_osname = config.os.c_str();
140 GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(config.sysroot);
141 GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);
143 if (error !=
nullptr) {
144 LOG_ERROR <<
"could not get repo";
149 auto origin = StructGuard<GKeyFile>(
150 ostree_sysroot_origin_new_from_refspec(sysroot.get(), target.sha256Hash().c_str()), g_key_file_free);
151 if (ostree_repo_resolve_rev(repo.get(), target.sha256Hash().c_str(), FALSE, &revision, &error) == 0) {
152 LOG_ERROR << error->message;
158 GObjectUniquePtr<OstreeDeployment> merge_deployment(ostree_sysroot_get_merge_deployment(sysroot.get(), opt_osname));
159 if (merge_deployment ==
nullptr) {
160 LOG_ERROR <<
"No merge deployment";
164 if (ostree_sysroot_prepare_cleanup(sysroot.get(), cancellable, &error) == 0) {
165 LOG_ERROR << error->message;
171 std::string args_content =
172 std::string(ostree_bootconfig_parser_get(ostree_deployment_get_bootconfig(merge_deployment.get()),
"options"));
173 std::vector<std::string> args_vector;
175 boost::split(args_vector, args_content, boost::is_any_of(
" "));
177 std::vector<const char *> kargs_strv_vector;
178 kargs_strv_vector.reserve(args_vector.size() + 1);
180 for (
auto it = args_vector.begin(); it != args_vector.end(); ++it) {
181 kargs_strv_vector.push_back((*it).c_str());
183 kargs_strv_vector[args_vector.size()] =
nullptr;
184 auto *kargs_strv =
const_cast<char **
>(&kargs_strv_vector[0]);
186 OstreeDeployment *new_deployment_raw =
nullptr;
187 if (ostree_sysroot_deploy_tree(sysroot.get(), opt_osname, revision, origin.get(), merge_deployment.get(), kargs_strv,
188 &new_deployment_raw, cancellable, &error) == 0) {
189 LOG_ERROR <<
"ostree_sysroot_deploy_tree: " << error->message;
194 GObjectUniquePtr<OstreeDeployment> new_deployment = GObjectUniquePtr<OstreeDeployment>(new_deployment_raw);
196 if (ostree_sysroot_simple_write_deployment(sysroot.get(),
nullptr, new_deployment.get(), merge_deployment.get(),
197 OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NONE, cancellable,
199 LOG_ERROR <<
"ostree_sysroot_simple_write_deployment:" << error->message;
206 if (bootloader_ !=
nullptr) {
207 bootloader_->rebootFlagSet();
210 LOG_INFO <<
"Performing sync()";
215 void OstreeManager::completeInstall()
const {
216 LOG_INFO <<
"About to reboot the system in order to apply pending updates...";
217 bootloader_->reboot();
221 if (!bootloader_->rebootDetected()) {
223 "Reboot is required for the pending update application");
226 LOG_INFO <<
"Checking installation of new OSTree sysroot";
227 const std::string current_hash = getCurrentHash();
232 if (current_hash != target.sha256Hash()) {
233 LOG_ERROR <<
"Expected to boot " << target.sha256Hash() <<
" but found " << current_hash
234 <<
". The system may have been rolled back.";
238 bootloader_->rebootFlagClear();
239 return install_result;
242 void OstreeManager::updateNotify() { bootloader_->updateNotify(); }
245 const std::shared_ptr<INvStorage> &storage,
const std::shared_ptr<HttpInterface> &http)
247 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
248 if (sysroot_smart ==
nullptr) {
249 throw std::runtime_error(
"Could not find OSTree sysroot at: " + config.sysroot.string());
254 if (imageUpdated()) {
255 bootloader_->setBootOK();
259 OstreeManager::~OstreeManager() { bootloader_.reset(
nullptr); }
266 return PackageManagerInterface::fetchTarget(target, fetcher, keys, progress_cb, token);
268 return OstreeManager::pull(config.sysroot, config.ostree_server, keys, target, token, progress_cb).success;
271 TargetStatus OstreeManager::verifyTarget(
const Uptane::Target &target)
const {
275 return PackageManagerInterface::verifyTarget(target);
277 return verifyTargetInternal(target);
280 TargetStatus OstreeManager::verifyTargetInternal(
const Uptane::Target &target)
const {
281 const std::string refhash = target.sha256Hash();
282 GError *error =
nullptr;
284 GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(config.sysroot);
285 GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);
286 if (error !=
nullptr) {
287 LOG_ERROR <<
"Could not get OSTree repo";
289 return TargetStatus::kNotFound;
292 GHashTable *ref_list =
nullptr;
293 if (ostree_repo_list_commit_objects_starting_with(repo.get(), refhash.c_str(), &ref_list,
nullptr, &error) != 0) {
294 guint length = g_hash_table_size(ref_list);
295 g_hash_table_destroy(ref_list);
298 return TargetStatus::kGood;
301 if (error !=
nullptr) {
306 LOG_ERROR <<
"Could not find OSTree commit";
307 return TargetStatus::kNotFound;
310 Json::Value OstreeManager::getInstalledPackages()
const {
311 std::string packages_str = Utils::readFile(config.packages_file);
312 std::vector<std::string> package_lines;
314 boost::split(package_lines, packages_str, boost::is_any_of(
"\n"));
315 Json::Value packages(Json::arrayValue);
316 for (
auto it = package_lines.begin(); it != package_lines.end(); ++it) {
320 size_t pos = it->find(
" ");
321 if (pos == std::string::npos) {
322 throw std::runtime_error(
"Wrong packages file format");
325 package[
"name"] = it->substr(0, pos);
326 package[
"version"] = it->substr(pos + 1);
327 packages.append(package);
332 std::string OstreeManager::getCurrentHash()
const {
333 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
334 OstreeDeployment *booted_deployment = ostree_sysroot_get_booted_deployment(sysroot_smart.get());
335 if (booted_deployment ==
nullptr) {
336 throw std::runtime_error(
"Could not get booted deployment in " + config.sysroot.string());
338 return ostree_deployment_get_csum(booted_deployment);
342 const std::string current_hash = getCurrentHash();
343 boost::optional<Uptane::Target> current_version;
346 storage_->loadPrimaryInstalledVersions(¤t_version,
nullptr);
348 if (!!current_version && current_version->sha256Hash() == current_hash) {
349 return *current_version;
352 LOG_ERROR <<
"Current versions in storage and reported by OSTree do not match";
356 std::vector<Uptane::Target> installed_versions;
357 storage_->loadPrimaryInstallationLog(&installed_versions,
false);
363 std::vector<Uptane::Target>::reverse_iterator it;
364 for (it = installed_versions.rbegin(); it != installed_versions.rend(); it++) {
365 if (it->sha256Hash() == current_hash) {
370 return Uptane::Target::Unknown();
374 bool OstreeManager::imageUpdated() {
375 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
378 GPtrArray *deployments = ostree_sysroot_get_deployments(sysroot_smart.get());
380 OstreeDeployment *pending_deployment =
nullptr;
381 ostree_sysroot_query_deployments_for(sysroot_smart.get(),
nullptr, &pending_deployment,
nullptr);
383 bool pending_found =
false;
384 for (guint i = 0; i < deployments->len; i++) {
386 if (deployments->pdata[i] == pending_deployment) {
387 pending_found =
true;
392 g_ptr_array_unref(deployments);
393 return !pending_found;
396 GObjectUniquePtr<OstreeDeployment> OstreeManager::getStagedDeployment()
const {
397 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
399 GPtrArray *deployments =
nullptr;
400 OstreeDeployment *res =
nullptr;
402 deployments = ostree_sysroot_get_deployments(sysroot_smart.get());
404 if (deployments->len > 0) {
406 auto *d =
static_cast<OstreeDeployment *
>(deployments->pdata[0]);
407 auto *d2 =
static_cast<OstreeDeployment *
>(g_object_ref(d));
411 g_ptr_array_unref(deployments);
412 return GObjectUniquePtr<OstreeDeployment>(res);
415 GObjectUniquePtr<OstreeSysroot> OstreeManager::LoadSysroot(
const boost::filesystem::path &path) {
416 GObjectUniquePtr<OstreeSysroot> sysroot =
nullptr;
419 GFile *fl = g_file_new_for_path(path.c_str());
420 sysroot.reset(ostree_sysroot_new(fl));
423 sysroot.reset(ostree_sysroot_new_default());
425 GError *error =
nullptr;
426 if (ostree_sysroot_load(sysroot.get(),
nullptr, &error) == 0) {
427 if (error !=
nullptr) {
430 throw std::runtime_error(
"could not load sysroot");
435 GObjectUniquePtr<OstreeRepo> OstreeManager::LoadRepo(OstreeSysroot *sysroot, GError **error) {
436 OstreeRepo *repo =
nullptr;
438 if (ostree_sysroot_get_repo(sysroot, &repo,
nullptr, error) == 0) {
442 return GObjectUniquePtr<OstreeRepo>(repo);
445 bool OstreeManager::addRemote(OstreeRepo *repo,
const std::string &url,
const KeyManager &keys) {
446 GCancellable *cancellable =
nullptr;
447 GError *error =
nullptr;
451 g_variant_builder_init(&b, G_VARIANT_TYPE(
"a{sv}"));
452 g_variant_builder_add(&b,
"{s@v}",
"gpg-verify", g_variant_new_variant(g_variant_new_boolean(FALSE)));
454 std::string cert_file = keys.getCertFile();
455 std::string pkey_file = keys.getPkeyFile();
456 std::string ca_file = keys.getCaFile();
457 if (!cert_file.empty() && !pkey_file.empty() && !ca_file.empty()) {
458 g_variant_builder_add(&b,
"{s@v}",
"tls-client-cert-path",
459 g_variant_new_variant(g_variant_new_string(cert_file.c_str())));
460 g_variant_builder_add(&b,
"{s@v}",
"tls-client-key-path",
461 g_variant_new_variant(g_variant_new_string(pkey_file.c_str())));
462 g_variant_builder_add(&b,
"{s@v}",
"tls-ca-path", g_variant_new_variant(g_variant_new_string(ca_file.c_str())));
464 options = g_variant_builder_end(&b);
466 if (ostree_repo_remote_change(repo,
nullptr, OSTREE_REPO_REMOTE_CHANGE_DELETE_IF_EXISTS, remote, url.c_str(), options,
467 cancellable, &error) == 0) {
468 LOG_ERROR <<
"Error of adding remote: " << error->message;
470 g_variant_unref(options);
473 if (ostree_repo_remote_change(repo,
nullptr, OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS, remote, url.c_str(),
474 options, cancellable, &error) == 0) {
475 LOG_ERROR <<
"Error of adding remote: " << error->message;
477 g_variant_unref(options);
480 g_variant_unref(options);