1 #include "package_manager/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 "logging/logging.h"
17 #include "utilities/utils.h"
19 static void aktualizr_progress_cb(OstreeAsyncProgress *progress, gpointer
data) {
20 auto *mt = static_cast<PullMetaStruct *>(
data);
21 if (mt->token !=
nullptr && !mt->token->canContinue()) {
22 g_cancellable_cancel(mt->cancellable.get());
25 g_autofree
char *status = ostree_async_progress_get_status(progress);
26 guint scanning = ostree_async_progress_get_uint(progress,
"scanning");
27 guint outstanding_fetches = ostree_async_progress_get_uint(progress,
"outstanding-fetches");
28 guint outstanding_metadata_fetches = ostree_async_progress_get_uint(progress,
"outstanding-metadata-fetches");
29 guint outstanding_writes = ostree_async_progress_get_uint(progress,
"outstanding-writes");
30 guint n_scanned_metadata = ostree_async_progress_get_uint(progress,
"scanned-metadata");
32 if (status !=
nullptr && *status !=
'\0') {
33 LOG_INFO <<
"ostree-pull: " << status;
34 }
else if (outstanding_fetches != 0) {
35 guint fetched = ostree_async_progress_get_uint(progress,
"fetched");
36 guint metadata_fetched = ostree_async_progress_get_uint(progress,
"metadata-fetched");
37 guint requested = ostree_async_progress_get_uint(progress,
"requested");
38 if (scanning != 0 || outstanding_metadata_fetches != 0) {
39 LOG_INFO <<
"ostree-pull: Receiving metadata objects: " << metadata_fetched
40 <<
" outstanding: " << outstanding_metadata_fetches;
41 if (mt->progress_cb) {
42 mt->progress_cb(mt->target,
"Receiving metadata objects", 0);
45 guint calculated = (fetched * 100) / requested;
46 if (calculated != mt->percent_complete) {
47 mt->percent_complete = calculated;
48 LOG_INFO <<
"ostree-pull: Receiving objects: " << calculated <<
"% ";
49 if (mt->progress_cb) {
50 mt->progress_cb(mt->target,
"Receiving objects", calculated);
54 }
else if (outstanding_writes != 0) {
55 LOG_INFO <<
"ostree-pull: Writing objects: " << outstanding_writes;
57 LOG_INFO <<
"ostree-pull: Scanning metadata: " << n_scanned_metadata;
58 if (mt->progress_cb) {
59 mt->progress_cb(mt->target,
"Scanning metadata", 0);
65 const std::string &ostree_server,
const KeyManager &keys,
67 OstreeProgressCb progress_cb) {
68 const std::string refhash = target.sha256Hash();
69 const char *
const commit_ids[] = {refhash.c_str()};
70 GError *error =
nullptr;
71 GVariantBuilder builder;
73 GObjectUniquePtr<OstreeAsyncProgress> progress =
nullptr;
75 GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(sysroot_path);
76 GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);
77 if (error !=
nullptr) {
78 LOG_ERROR <<
"Could not get OSTree repo";
83 GHashTable *ref_list =
nullptr;
84 if (ostree_repo_list_commit_objects_starting_with(repo.get(), refhash.c_str(), &ref_list,
nullptr, &error) != 0) {
85 guint length = g_hash_table_size(ref_list);
86 g_hash_table_destroy(ref_list);
89 LOG_DEBUG <<
"refhash already pulled";
93 if (error !=
nullptr) {
98 if (!OstreeManager::addRemote(repo.get(), ostree_server, keys)) {
102 g_variant_builder_init(&builder, G_VARIANT_TYPE(
"a{sv}"));
103 g_variant_builder_add(&builder,
"{s@v}",
"flags", g_variant_new_variant(g_variant_new_int32(0)));
105 g_variant_builder_add(&builder,
"{s@v}",
"refs", g_variant_new_variant(g_variant_new_strv(commit_ids, 1)));
107 options = g_variant_builder_end(&builder);
109 PullMetaStruct mt(target, token, g_cancellable_new(), std::move(progress_cb));
110 progress.reset(ostree_async_progress_new_and_connect(aktualizr_progress_cb, &mt));
111 if (ostree_repo_pull_with_options(repo.get(), remote, options, progress.get(), mt.cancellable.get(), &error) == 0) {
112 LOG_ERROR <<
"Error while pulling image: " << error->code <<
" " << error->message;
115 g_variant_unref(options);
118 ostree_async_progress_finish(progress.get());
119 g_variant_unref(options);
124 const char *opt_osname =
nullptr;
125 GCancellable *cancellable =
nullptr;
126 GError *error =
nullptr;
127 g_autofree
char *revision =
nullptr;
129 if (config.os.size() != 0u) {
130 opt_osname = config.os.c_str();
133 GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(config.sysroot);
134 GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);
136 if (error !=
nullptr) {
137 LOG_ERROR <<
"could not get repo";
142 auto origin = StructGuard<GKeyFile>(
143 ostree_sysroot_origin_new_from_refspec(sysroot.get(), target.sha256Hash().c_str()), g_key_file_free);
144 if (ostree_repo_resolve_rev(repo.get(), target.sha256Hash().c_str(), FALSE, &revision, &error) == 0) {
145 LOG_ERROR << error->message;
151 GObjectUniquePtr<OstreeDeployment> merge_deployment(ostree_sysroot_get_merge_deployment(sysroot.get(), opt_osname));
152 if (merge_deployment ==
nullptr) {
153 LOG_ERROR <<
"No merge deployment";
157 if (ostree_sysroot_prepare_cleanup(sysroot.get(), cancellable, &error) == 0) {
158 LOG_ERROR << error->message;
164 std::string args_content =
165 std::string(ostree_bootconfig_parser_get(ostree_deployment_get_bootconfig(merge_deployment.get()),
"options"));
166 std::vector<std::string> args_vector;
167 boost::split(args_vector, args_content, boost::is_any_of(
" "));
169 std::vector<const char *> kargs_strv_vector;
170 kargs_strv_vector.reserve(args_vector.size() + 1);
172 for (
auto it = args_vector.begin(); it != args_vector.end(); ++it) {
173 kargs_strv_vector.push_back((*it).c_str());
175 kargs_strv_vector[args_vector.size()] =
nullptr;
176 auto kargs_strv = const_cast<char **>(&kargs_strv_vector[0]);
178 OstreeDeployment *new_deployment_raw =
nullptr;
179 if (ostree_sysroot_deploy_tree(sysroot.get(), opt_osname, revision, origin.get(), merge_deployment.get(), kargs_strv,
180 &new_deployment_raw, cancellable, &error) == 0) {
181 LOG_ERROR <<
"ostree_sysroot_deploy_tree: " << error->message;
186 GObjectUniquePtr<OstreeDeployment> new_deployment = GObjectUniquePtr<OstreeDeployment>(new_deployment_raw);
188 if (ostree_sysroot_simple_write_deployment(sysroot.get(),
nullptr, new_deployment.get(), merge_deployment.get(),
189 OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NONE, cancellable,
191 LOG_ERROR <<
"ostree_sysroot_simple_write_deployment:" << error->message;
198 if (bootloader_ !=
nullptr) {
199 bootloader_->rebootFlagSet();
202 LOG_INFO <<
"Performing sync()";
207 void OstreeManager::completeInstall()
const {
208 LOG_INFO <<
"About to reboot the system in order to apply pending updates...";
209 bootloader_->reboot();
213 LOG_INFO <<
"Checking installation of new OSTree sysroot";
214 const std::string current_hash = getCurrentHash();
216 if (current_hash != target.sha256Hash()) {
217 LOG_ERROR <<
"Expected to boot " << target.sha256Hash() <<
" but found " << current_hash
218 <<
". The system may have been rolled back.";
226 std::shared_ptr<HttpInterface> http)
228 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
229 if (sysroot_smart ==
nullptr) {
230 throw std::runtime_error(
"Could not find OSTree sysroot at: " + config.sysroot.string());
235 if (imageUpdated()) {
236 bootloader_->setBootOK();
245 return PackageManagerInterface::fetchTarget(target, fetcher, keys, progress_cb, token);
247 return OstreeManager::pull(config.sysroot, config.ostree_server, keys, target, token, progress_cb).success;
250 TargetStatus OstreeManager::verifyTarget(
const Uptane::Target &target)
const {
254 return PackageManagerInterface::verifyTarget(target);
256 return verifyTargetInternal(target);
259 TargetStatus OstreeManager::verifyTargetInternal(
const Uptane::Target &target)
const {
260 const std::string refhash = target.sha256Hash();
261 GError *error =
nullptr;
263 GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(config.sysroot);
264 GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);
265 if (error !=
nullptr) {
266 LOG_ERROR <<
"Could not get OSTree repo";
268 return TargetStatus::kNotFound;
271 GHashTable *ref_list =
nullptr;
272 if (ostree_repo_list_commit_objects_starting_with(repo.get(), refhash.c_str(), &ref_list,
nullptr, &error) != 0) {
273 guint length = g_hash_table_size(ref_list);
274 g_hash_table_destroy(ref_list);
277 return TargetStatus::kGood;
280 if (error !=
nullptr) {
285 LOG_ERROR <<
"Could not find OSTree commit";
286 return TargetStatus::kNotFound;
289 Json::Value OstreeManager::getInstalledPackages()
const {
290 std::string packages_str = Utils::readFile(config.packages_file);
291 std::vector<std::string> package_lines;
292 boost::split(package_lines, packages_str, boost::is_any_of(
"\n"));
293 Json::Value packages(Json::arrayValue);
294 for (
auto it = package_lines.begin(); it != package_lines.end(); ++it) {
298 size_t pos = it->find(
" ");
299 if (pos == std::string::npos) {
300 throw std::runtime_error(
"Wrong packages file format");
303 package[
"name"] = it->substr(0, pos);
304 package[
"version"] = it->substr(pos + 1);
305 packages.append(package);
310 std::string OstreeManager::getCurrentHash()
const {
311 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
312 OstreeDeployment *booted_deployment = ostree_sysroot_get_booted_deployment(sysroot_smart.get());
313 if (booted_deployment ==
nullptr) {
314 throw std::runtime_error(
"Could not get booted deployment in " + config.sysroot.string());
316 return ostree_deployment_get_csum(booted_deployment);
320 const std::string current_hash = getCurrentHash();
321 boost::optional<Uptane::Target> current_version;
324 storage_->loadPrimaryInstalledVersions(¤t_version,
nullptr);
326 if (!!current_version && current_version->sha256Hash() == current_hash) {
327 return *current_version;
330 LOG_ERROR <<
"Current versions in storage and reported by ostree do not match";
334 std::vector<Uptane::Target> installed_versions;
335 storage_->loadPrimaryInstallationLog(&installed_versions,
false);
341 std::vector<Uptane::Target>::reverse_iterator it;
342 for (it = installed_versions.rbegin(); it != installed_versions.rend(); it++) {
343 if (it->sha256Hash() == current_hash) {
348 return Uptane::Target::Unknown();
352 bool OstreeManager::imageUpdated() {
353 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
356 GPtrArray *deployments = ostree_sysroot_get_deployments(sysroot_smart.get());
358 OstreeDeployment *pending_deployment =
nullptr;
359 ostree_sysroot_query_deployments_for(sysroot_smart.get(),
nullptr, &pending_deployment,
nullptr);
361 bool pending_found =
false;
362 for (guint i = 0; i < deployments->len; i++) {
364 if (deployments->pdata[i] == pending_deployment) {
365 pending_found =
true;
370 g_ptr_array_unref(deployments);
371 return !pending_found;
374 GObjectUniquePtr<OstreeDeployment> OstreeManager::getStagedDeployment()
const {
375 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
377 GPtrArray *deployments =
nullptr;
378 OstreeDeployment *res =
nullptr;
380 deployments = ostree_sysroot_get_deployments(sysroot_smart.get());
382 if (deployments->len > 0) {
384 auto *d = static_cast<OstreeDeployment *>(deployments->pdata[0]);
385 auto *d2 = static_cast<OstreeDeployment *>(g_object_ref(d));
389 g_ptr_array_unref(deployments);
390 return GObjectUniquePtr<OstreeDeployment>(res);
393 GObjectUniquePtr<OstreeSysroot> OstreeManager::LoadSysroot(
const boost::filesystem::path &path) {
394 GObjectUniquePtr<OstreeSysroot> sysroot =
nullptr;
397 GFile *fl = g_file_new_for_path(path.c_str());
398 sysroot.reset(ostree_sysroot_new(fl));
401 sysroot.reset(ostree_sysroot_new_default());
403 GError *error =
nullptr;
404 if (ostree_sysroot_load(sysroot.get(),
nullptr, &error) == 0) {
405 if (error !=
nullptr) {
408 throw std::runtime_error(
"could not load sysroot");
413 GObjectUniquePtr<OstreeRepo> OstreeManager::LoadRepo(OstreeSysroot *sysroot, GError **error) {
414 OstreeRepo *repo =
nullptr;
416 if (ostree_sysroot_get_repo(sysroot, &repo,
nullptr, error) == 0) {
420 return GObjectUniquePtr<OstreeRepo>(repo);
423 bool OstreeManager::addRemote(OstreeRepo *repo,
const std::string &url,
const KeyManager &keys) {
424 GCancellable *cancellable =
nullptr;
425 GError *error =
nullptr;
429 g_variant_builder_init(&b, G_VARIANT_TYPE(
"a{sv}"));
430 g_variant_builder_add(&b,
"{s@v}",
"gpg-verify", g_variant_new_variant(g_variant_new_boolean(FALSE)));
432 std::string cert_file = keys.getCertFile();
433 std::string pkey_file = keys.getPkeyFile();
434 std::string ca_file = keys.getCaFile();
435 if (!cert_file.empty() && !pkey_file.empty() && !ca_file.empty()) {
436 g_variant_builder_add(&b,
"{s@v}",
"tls-client-cert-path",
437 g_variant_new_variant(g_variant_new_string(cert_file.c_str())));
438 g_variant_builder_add(&b,
"{s@v}",
"tls-client-key-path",
439 g_variant_new_variant(g_variant_new_string(pkey_file.c_str())));
440 g_variant_builder_add(&b,
"{s@v}",
"tls-ca-path", g_variant_new_variant(g_variant_new_string(ca_file.c_str())));
442 options = g_variant_builder_end(&b);
444 if (ostree_repo_remote_change(repo,
nullptr, OSTREE_REPO_REMOTE_CHANGE_DELETE_IF_EXISTS, remote, url.c_str(), options,
445 cancellable, &error) == 0) {
446 LOG_ERROR <<
"Error of adding remote: " << error->message;
448 g_variant_unref(options);
451 if (ostree_repo_remote_change(repo,
nullptr, OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS, remote, url.c_str(),
452 options, cancellable, &error) == 0) {
453 LOG_ERROR <<
"Error of adding remote: " << error->message;
455 g_variant_unref(options);
458 g_variant_unref(options);