1 #include "ostreemanager.h"
2 #include "packagemanagerfactory.h"
10 #include <ostree-async-progress.h>
11 #include <boost/algorithm/string.hpp>
12 #include <boost/algorithm/string/classification.hpp>
13 #include <boost/algorithm/string/split.hpp>
14 #include <boost/filesystem.hpp>
17 #include "logging/logging.h"
18 #include "utilities/utils.h"
20 AUTO_REGISTER_PACKAGE_MANAGER(PACKAGE_MANAGER_OSTREE,
OstreeManager);
22 static void aktualizr_progress_cb(OstreeAsyncProgress *progress, gpointer
data) {
23 auto *mt = static_cast<PullMetaStruct *>(
data);
24 if (mt->token !=
nullptr && !mt->token->canContinue()) {
25 g_cancellable_cancel(mt->cancellable.get());
28 g_autofree
char *status = ostree_async_progress_get_status(progress);
29 guint scanning = ostree_async_progress_get_uint(progress,
"scanning");
30 guint outstanding_fetches = ostree_async_progress_get_uint(progress,
"outstanding-fetches");
31 guint outstanding_metadata_fetches = ostree_async_progress_get_uint(progress,
"outstanding-metadata-fetches");
32 guint outstanding_writes = ostree_async_progress_get_uint(progress,
"outstanding-writes");
33 guint n_scanned_metadata = ostree_async_progress_get_uint(progress,
"scanned-metadata");
35 if (status !=
nullptr && *status !=
'\0') {
36 LOG_INFO <<
"ostree-pull: " << status;
37 }
else if (outstanding_fetches != 0) {
38 guint fetched = ostree_async_progress_get_uint(progress,
"fetched");
39 guint metadata_fetched = ostree_async_progress_get_uint(progress,
"metadata-fetched");
40 guint requested = ostree_async_progress_get_uint(progress,
"requested");
41 if (scanning != 0 || outstanding_metadata_fetches != 0) {
42 LOG_INFO <<
"ostree-pull: Receiving metadata objects: " << metadata_fetched
43 <<
" outstanding: " << outstanding_metadata_fetches;
44 if (mt->progress_cb) {
45 mt->progress_cb(mt->target,
"Receiving metadata objects", 0);
48 guint calculated = (fetched * 100) / requested;
49 if (calculated != mt->percent_complete) {
50 mt->percent_complete = calculated;
51 LOG_INFO <<
"ostree-pull: Receiving objects: " << calculated <<
"% ";
52 if (mt->progress_cb) {
53 mt->progress_cb(mt->target,
"Receiving objects", calculated);
57 }
else if (outstanding_writes != 0) {
58 LOG_INFO <<
"ostree-pull: Writing objects: " << outstanding_writes;
60 LOG_INFO <<
"ostree-pull: Scanning metadata: " << n_scanned_metadata;
61 if (mt->progress_cb) {
62 mt->progress_cb(mt->target,
"Scanning metadata", 0);
68 const std::string &ostree_server,
const KeyManager &keys,
70 OstreeProgressCb progress_cb) {
71 const std::string refhash = target.sha256Hash();
72 const char *
const commit_ids[] = {refhash.c_str()};
73 GError *error =
nullptr;
74 GVariantBuilder builder;
76 GObjectUniquePtr<OstreeAsyncProgress> progress =
nullptr;
78 GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(sysroot_path);
79 GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);
80 if (error !=
nullptr) {
81 LOG_ERROR <<
"Could not get OSTree repo";
86 GHashTable *ref_list =
nullptr;
87 if (ostree_repo_list_commit_objects_starting_with(repo.get(), refhash.c_str(), &ref_list,
nullptr, &error) != 0) {
88 guint length = g_hash_table_size(ref_list);
89 g_hash_table_destroy(ref_list);
92 LOG_DEBUG <<
"refhash already pulled";
96 if (error !=
nullptr) {
101 if (!OstreeManager::addRemote(repo.get(), ostree_server, keys)) {
105 g_variant_builder_init(&builder, G_VARIANT_TYPE(
"a{sv}"));
106 g_variant_builder_add(&builder,
"{s@v}",
"flags", g_variant_new_variant(g_variant_new_int32(0)));
108 g_variant_builder_add(&builder,
"{s@v}",
"refs", g_variant_new_variant(g_variant_new_strv(commit_ids, 1)));
110 options = g_variant_builder_end(&builder);
112 PullMetaStruct mt(target, token, g_cancellable_new(), std::move(progress_cb));
113 progress.reset(ostree_async_progress_new_and_connect(aktualizr_progress_cb, &mt));
114 if (ostree_repo_pull_with_options(repo.get(), remote, options, progress.get(), mt.cancellable.get(), &error) == 0) {
115 LOG_ERROR <<
"Error while pulling image: " << error->code <<
" " << error->message;
118 g_variant_unref(options);
121 ostree_async_progress_finish(progress.get());
122 g_variant_unref(options);
127 const char *opt_osname =
nullptr;
128 GCancellable *cancellable =
nullptr;
129 GError *error =
nullptr;
130 g_autofree
char *revision =
nullptr;
132 if (config.os.size() != 0u) {
133 opt_osname = config.os.c_str();
136 GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(config.sysroot);
137 GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);
139 if (error !=
nullptr) {
140 LOG_ERROR <<
"could not get repo";
145 auto origin = StructGuard<GKeyFile>(
146 ostree_sysroot_origin_new_from_refspec(sysroot.get(), target.sha256Hash().c_str()), g_key_file_free);
147 if (ostree_repo_resolve_rev(repo.get(), target.sha256Hash().c_str(), FALSE, &revision, &error) == 0) {
148 LOG_ERROR << error->message;
154 GObjectUniquePtr<OstreeDeployment> merge_deployment(ostree_sysroot_get_merge_deployment(sysroot.get(), opt_osname));
155 if (merge_deployment ==
nullptr) {
156 LOG_ERROR <<
"No merge deployment";
160 if (ostree_sysroot_prepare_cleanup(sysroot.get(), cancellable, &error) == 0) {
161 LOG_ERROR << error->message;
167 std::string args_content =
168 std::string(ostree_bootconfig_parser_get(ostree_deployment_get_bootconfig(merge_deployment.get()),
"options"));
169 std::vector<std::string> args_vector;
170 boost::split(args_vector, args_content, boost::is_any_of(
" "));
172 std::vector<const char *> kargs_strv_vector;
173 kargs_strv_vector.reserve(args_vector.size() + 1);
175 for (
auto it = args_vector.begin(); it != args_vector.end(); ++it) {
176 kargs_strv_vector.push_back((*it).c_str());
178 kargs_strv_vector[args_vector.size()] =
nullptr;
179 auto kargs_strv = const_cast<char **>(&kargs_strv_vector[0]);
181 OstreeDeployment *new_deployment_raw =
nullptr;
182 if (ostree_sysroot_deploy_tree(sysroot.get(), opt_osname, revision, origin.get(), merge_deployment.get(), kargs_strv,
183 &new_deployment_raw, cancellable, &error) == 0) {
184 LOG_ERROR <<
"ostree_sysroot_deploy_tree: " << error->message;
189 GObjectUniquePtr<OstreeDeployment> new_deployment = GObjectUniquePtr<OstreeDeployment>(new_deployment_raw);
191 if (ostree_sysroot_simple_write_deployment(sysroot.get(),
nullptr, new_deployment.get(), merge_deployment.get(),
192 OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NONE, cancellable,
194 LOG_ERROR <<
"ostree_sysroot_simple_write_deployment:" << error->message;
201 if (bootloader_ !=
nullptr) {
202 bootloader_->rebootFlagSet();
205 LOG_INFO <<
"Performing sync()";
210 void OstreeManager::completeInstall()
const {
211 LOG_INFO <<
"About to reboot the system in order to apply pending updates...";
212 bootloader_->reboot();
216 if (!bootloader_->rebootDetected()) {
218 "Reboot is required for the pending update application");
221 LOG_INFO <<
"Checking installation of new OSTree sysroot";
222 const std::string current_hash = getCurrentHash();
227 if (current_hash != target.sha256Hash()) {
228 LOG_ERROR <<
"Expected to boot " << target.sha256Hash() <<
" but found " << current_hash
229 <<
". The system may have been rolled back.";
233 bootloader_->rebootFlagClear();
234 return install_result;
238 const std::shared_ptr<INvStorage> &storage,
const std::shared_ptr<HttpInterface> &http)
240 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
241 if (sysroot_smart ==
nullptr) {
242 throw std::runtime_error(
"Could not find OSTree sysroot at: " + config.sysroot.string());
247 if (imageUpdated()) {
248 bootloader_->setBootOK();
257 return PackageManagerInterface::fetchTarget(target, fetcher, keys, progress_cb, token);
259 return OstreeManager::pull(config.sysroot, config.ostree_server, keys, target, token, progress_cb).success;
262 TargetStatus OstreeManager::verifyTarget(
const Uptane::Target &target)
const {
266 return PackageManagerInterface::verifyTarget(target);
268 return verifyTargetInternal(target);
271 TargetStatus OstreeManager::verifyTargetInternal(
const Uptane::Target &target)
const {
272 const std::string refhash = target.sha256Hash();
273 GError *error =
nullptr;
275 GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(config.sysroot);
276 GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);
277 if (error !=
nullptr) {
278 LOG_ERROR <<
"Could not get OSTree repo";
280 return TargetStatus::kNotFound;
283 GHashTable *ref_list =
nullptr;
284 if (ostree_repo_list_commit_objects_starting_with(repo.get(), refhash.c_str(), &ref_list,
nullptr, &error) != 0) {
285 guint length = g_hash_table_size(ref_list);
286 g_hash_table_destroy(ref_list);
289 return TargetStatus::kGood;
292 if (error !=
nullptr) {
297 LOG_ERROR <<
"Could not find OSTree commit";
298 return TargetStatus::kNotFound;
301 Json::Value OstreeManager::getInstalledPackages()
const {
302 std::string packages_str = Utils::readFile(config.packages_file);
303 std::vector<std::string> package_lines;
304 boost::split(package_lines, packages_str, boost::is_any_of(
"\n"));
305 Json::Value packages(Json::arrayValue);
306 for (
auto it = package_lines.begin(); it != package_lines.end(); ++it) {
310 size_t pos = it->find(
" ");
311 if (pos == std::string::npos) {
312 throw std::runtime_error(
"Wrong packages file format");
315 package[
"name"] = it->substr(0, pos);
316 package[
"version"] = it->substr(pos + 1);
317 packages.append(package);
322 std::string OstreeManager::getCurrentHash()
const {
323 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
324 OstreeDeployment *booted_deployment = ostree_sysroot_get_booted_deployment(sysroot_smart.get());
325 if (booted_deployment ==
nullptr) {
326 throw std::runtime_error(
"Could not get booted deployment in " + config.sysroot.string());
328 return ostree_deployment_get_csum(booted_deployment);
332 const std::string current_hash = getCurrentHash();
333 boost::optional<Uptane::Target> current_version;
336 storage_->loadPrimaryInstalledVersions(¤t_version,
nullptr);
338 if (!!current_version && current_version->sha256Hash() == current_hash) {
339 return *current_version;
342 LOG_ERROR <<
"Current versions in storage and reported by ostree do not match";
346 std::vector<Uptane::Target> installed_versions;
347 storage_->loadPrimaryInstallationLog(&installed_versions,
false);
353 std::vector<Uptane::Target>::reverse_iterator it;
354 for (it = installed_versions.rbegin(); it != installed_versions.rend(); it++) {
355 if (it->sha256Hash() == current_hash) {
360 return Uptane::Target::Unknown();
364 bool OstreeManager::imageUpdated() {
365 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
368 GPtrArray *deployments = ostree_sysroot_get_deployments(sysroot_smart.get());
370 OstreeDeployment *pending_deployment =
nullptr;
371 ostree_sysroot_query_deployments_for(sysroot_smart.get(),
nullptr, &pending_deployment,
nullptr);
373 bool pending_found =
false;
374 for (guint i = 0; i < deployments->len; i++) {
376 if (deployments->pdata[i] == pending_deployment) {
377 pending_found =
true;
382 g_ptr_array_unref(deployments);
383 return !pending_found;
386 GObjectUniquePtr<OstreeDeployment> OstreeManager::getStagedDeployment()
const {
387 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
389 GPtrArray *deployments =
nullptr;
390 OstreeDeployment *res =
nullptr;
392 deployments = ostree_sysroot_get_deployments(sysroot_smart.get());
394 if (deployments->len > 0) {
396 auto *d = static_cast<OstreeDeployment *>(deployments->pdata[0]);
397 auto *d2 = static_cast<OstreeDeployment *>(g_object_ref(d));
401 g_ptr_array_unref(deployments);
402 return GObjectUniquePtr<OstreeDeployment>(res);
405 GObjectUniquePtr<OstreeSysroot> OstreeManager::LoadSysroot(
const boost::filesystem::path &path) {
406 GObjectUniquePtr<OstreeSysroot> sysroot =
nullptr;
409 GFile *fl = g_file_new_for_path(path.c_str());
410 sysroot.reset(ostree_sysroot_new(fl));
413 sysroot.reset(ostree_sysroot_new_default());
415 GError *error =
nullptr;
416 if (ostree_sysroot_load(sysroot.get(),
nullptr, &error) == 0) {
417 if (error !=
nullptr) {
420 throw std::runtime_error(
"could not load sysroot");
425 GObjectUniquePtr<OstreeRepo> OstreeManager::LoadRepo(OstreeSysroot *sysroot, GError **error) {
426 OstreeRepo *repo =
nullptr;
428 if (ostree_sysroot_get_repo(sysroot, &repo,
nullptr, error) == 0) {
432 return GObjectUniquePtr<OstreeRepo>(repo);
435 bool OstreeManager::addRemote(OstreeRepo *repo,
const std::string &url,
const KeyManager &keys) {
436 GCancellable *cancellable =
nullptr;
437 GError *error =
nullptr;
441 g_variant_builder_init(&b, G_VARIANT_TYPE(
"a{sv}"));
442 g_variant_builder_add(&b,
"{s@v}",
"gpg-verify", g_variant_new_variant(g_variant_new_boolean(FALSE)));
444 std::string cert_file = keys.getCertFile();
445 std::string pkey_file = keys.getPkeyFile();
446 std::string ca_file = keys.getCaFile();
447 if (!cert_file.empty() && !pkey_file.empty() && !ca_file.empty()) {
448 g_variant_builder_add(&b,
"{s@v}",
"tls-client-cert-path",
449 g_variant_new_variant(g_variant_new_string(cert_file.c_str())));
450 g_variant_builder_add(&b,
"{s@v}",
"tls-client-key-path",
451 g_variant_new_variant(g_variant_new_string(pkey_file.c_str())));
452 g_variant_builder_add(&b,
"{s@v}",
"tls-ca-path", g_variant_new_variant(g_variant_new_string(ca_file.c_str())));
454 options = g_variant_builder_end(&b);
456 if (ostree_repo_remote_change(repo,
nullptr, OSTREE_REPO_REMOTE_CHANGE_DELETE_IF_EXISTS, remote, url.c_str(), options,
457 cancellable, &error) == 0) {
458 LOG_ERROR <<
"Error of adding remote: " << error->message;
460 g_variant_unref(options);
463 if (ostree_repo_remote_change(repo,
nullptr, OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS, remote, url.c_str(),
464 options, cancellable, &error) == 0) {
465 LOG_ERROR <<
"Error of adding remote: " << error->message;
467 g_variant_unref(options);
470 g_variant_unref(options);