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) {
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;
171 boost::split(args_vector, args_content, boost::is_any_of(
" "));
173 std::vector<const char *> kargs_strv_vector;
174 kargs_strv_vector.reserve(args_vector.size() + 1);
176 for (
auto it = args_vector.begin(); it != args_vector.end(); ++it) {
177 kargs_strv_vector.push_back((*it).c_str());
179 kargs_strv_vector[args_vector.size()] =
nullptr;
180 auto kargs_strv =
const_cast<char **
>(&kargs_strv_vector[0]);
182 OstreeDeployment *new_deployment_raw =
nullptr;
183 if (ostree_sysroot_deploy_tree(sysroot.get(), opt_osname, revision, origin.get(), merge_deployment.get(), kargs_strv,
184 &new_deployment_raw, cancellable, &error) == 0) {
185 LOG_ERROR <<
"ostree_sysroot_deploy_tree: " << error->message;
190 GObjectUniquePtr<OstreeDeployment> new_deployment = GObjectUniquePtr<OstreeDeployment>(new_deployment_raw);
192 if (ostree_sysroot_simple_write_deployment(sysroot.get(),
nullptr, new_deployment.get(), merge_deployment.get(),
193 OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NONE, cancellable,
195 LOG_ERROR <<
"ostree_sysroot_simple_write_deployment:" << error->message;
202 if (bootloader_ !=
nullptr) {
203 bootloader_->rebootFlagSet();
206 LOG_INFO <<
"Performing sync()";
211 void OstreeManager::completeInstall()
const {
212 LOG_INFO <<
"About to reboot the system in order to apply pending updates...";
213 bootloader_->reboot();
217 if (!bootloader_->rebootDetected()) {
219 "Reboot is required for the pending update application");
222 LOG_INFO <<
"Checking installation of new OSTree sysroot";
223 const std::string current_hash = getCurrentHash();
228 if (current_hash != target.sha256Hash()) {
229 LOG_ERROR <<
"Expected to boot " << target.sha256Hash() <<
" but found " << current_hash
230 <<
". The system may have been rolled back.";
234 bootloader_->rebootFlagClear();
235 return install_result;
239 const std::shared_ptr<INvStorage> &storage,
const std::shared_ptr<HttpInterface> &http)
241 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
242 if (sysroot_smart ==
nullptr) {
243 throw std::runtime_error(
"Could not find OSTree sysroot at: " + config.sysroot.string());
248 if (imageUpdated()) {
249 bootloader_->setBootOK();
258 return PackageManagerInterface::fetchTarget(target, fetcher, keys, progress_cb, token);
260 return OstreeManager::pull(config.sysroot, config.ostree_server, keys, target, token, progress_cb).success;
263 TargetStatus OstreeManager::verifyTarget(
const Uptane::Target &target)
const {
267 return PackageManagerInterface::verifyTarget(target);
269 return verifyTargetInternal(target);
272 TargetStatus OstreeManager::verifyTargetInternal(
const Uptane::Target &target)
const {
273 const std::string refhash = target.sha256Hash();
274 GError *error =
nullptr;
276 GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(config.sysroot);
277 GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);
278 if (error !=
nullptr) {
279 LOG_ERROR <<
"Could not get OSTree repo";
281 return TargetStatus::kNotFound;
284 GHashTable *ref_list =
nullptr;
285 if (ostree_repo_list_commit_objects_starting_with(repo.get(), refhash.c_str(), &ref_list,
nullptr, &error) != 0) {
286 guint length = g_hash_table_size(ref_list);
287 g_hash_table_destroy(ref_list);
290 return TargetStatus::kGood;
293 if (error !=
nullptr) {
298 LOG_ERROR <<
"Could not find OSTree commit";
299 return TargetStatus::kNotFound;
302 Json::Value OstreeManager::getInstalledPackages()
const {
303 std::string packages_str = Utils::readFile(config.packages_file);
304 std::vector<std::string> package_lines;
306 boost::split(package_lines, packages_str, boost::is_any_of(
"\n"));
307 Json::Value packages(Json::arrayValue);
308 for (
auto it = package_lines.begin(); it != package_lines.end(); ++it) {
312 size_t pos = it->find(
" ");
313 if (pos == std::string::npos) {
314 throw std::runtime_error(
"Wrong packages file format");
317 package[
"name"] = it->substr(0, pos);
318 package[
"version"] = it->substr(pos + 1);
319 packages.append(package);
324 std::string OstreeManager::getCurrentHash()
const {
325 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
326 OstreeDeployment *booted_deployment = ostree_sysroot_get_booted_deployment(sysroot_smart.get());
327 if (booted_deployment ==
nullptr) {
328 throw std::runtime_error(
"Could not get booted deployment in " + config.sysroot.string());
330 return ostree_deployment_get_csum(booted_deployment);
334 const std::string current_hash = getCurrentHash();
335 boost::optional<Uptane::Target> current_version;
338 storage_->loadPrimaryInstalledVersions(¤t_version,
nullptr);
340 if (!!current_version && current_version->sha256Hash() == current_hash) {
341 return *current_version;
344 LOG_ERROR <<
"Current versions in storage and reported by OSTree do not match";
348 std::vector<Uptane::Target> installed_versions;
349 storage_->loadPrimaryInstallationLog(&installed_versions,
false);
355 std::vector<Uptane::Target>::reverse_iterator it;
356 for (it = installed_versions.rbegin(); it != installed_versions.rend(); it++) {
357 if (it->sha256Hash() == current_hash) {
362 return Uptane::Target::Unknown();
366 bool OstreeManager::imageUpdated() {
367 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
370 GPtrArray *deployments = ostree_sysroot_get_deployments(sysroot_smart.get());
372 OstreeDeployment *pending_deployment =
nullptr;
373 ostree_sysroot_query_deployments_for(sysroot_smart.get(),
nullptr, &pending_deployment,
nullptr);
375 bool pending_found =
false;
376 for (guint i = 0; i < deployments->len; i++) {
378 if (deployments->pdata[i] == pending_deployment) {
379 pending_found =
true;
384 g_ptr_array_unref(deployments);
385 return !pending_found;
388 GObjectUniquePtr<OstreeDeployment> OstreeManager::getStagedDeployment()
const {
389 GObjectUniquePtr<OstreeSysroot> sysroot_smart = OstreeManager::LoadSysroot(config.sysroot);
391 GPtrArray *deployments =
nullptr;
392 OstreeDeployment *res =
nullptr;
394 deployments = ostree_sysroot_get_deployments(sysroot_smart.get());
396 if (deployments->len > 0) {
398 auto *d =
static_cast<OstreeDeployment *
>(deployments->pdata[0]);
399 auto *d2 =
static_cast<OstreeDeployment *
>(g_object_ref(d));
403 g_ptr_array_unref(deployments);
404 return GObjectUniquePtr<OstreeDeployment>(res);
407 GObjectUniquePtr<OstreeSysroot> OstreeManager::LoadSysroot(
const boost::filesystem::path &path) {
408 GObjectUniquePtr<OstreeSysroot> sysroot =
nullptr;
411 GFile *fl = g_file_new_for_path(path.c_str());
412 sysroot.reset(ostree_sysroot_new(fl));
415 sysroot.reset(ostree_sysroot_new_default());
417 GError *error =
nullptr;
418 if (ostree_sysroot_load(sysroot.get(),
nullptr, &error) == 0) {
419 if (error !=
nullptr) {
422 throw std::runtime_error(
"could not load sysroot");
427 GObjectUniquePtr<OstreeRepo> OstreeManager::LoadRepo(OstreeSysroot *sysroot, GError **error) {
428 OstreeRepo *repo =
nullptr;
430 if (ostree_sysroot_get_repo(sysroot, &repo,
nullptr, error) == 0) {
434 return GObjectUniquePtr<OstreeRepo>(repo);
437 bool OstreeManager::addRemote(OstreeRepo *repo,
const std::string &url,
const KeyManager &keys) {
438 GCancellable *cancellable =
nullptr;
439 GError *error =
nullptr;
443 g_variant_builder_init(&b, G_VARIANT_TYPE(
"a{sv}"));
444 g_variant_builder_add(&b,
"{s@v}",
"gpg-verify", g_variant_new_variant(g_variant_new_boolean(FALSE)));
446 std::string cert_file = keys.getCertFile();
447 std::string pkey_file = keys.getPkeyFile();
448 std::string ca_file = keys.getCaFile();
449 if (!cert_file.empty() && !pkey_file.empty() && !ca_file.empty()) {
450 g_variant_builder_add(&b,
"{s@v}",
"tls-client-cert-path",
451 g_variant_new_variant(g_variant_new_string(cert_file.c_str())));
452 g_variant_builder_add(&b,
"{s@v}",
"tls-client-key-path",
453 g_variant_new_variant(g_variant_new_string(pkey_file.c_str())));
454 g_variant_builder_add(&b,
"{s@v}",
"tls-ca-path", g_variant_new_variant(g_variant_new_string(ca_file.c_str())));
456 options = g_variant_builder_end(&b);
458 if (ostree_repo_remote_change(repo,
nullptr, OSTREE_REPO_REMOTE_CHANGE_DELETE_IF_EXISTS, remote, url.c_str(), options,
459 cancellable, &error) == 0) {
460 LOG_ERROR <<
"Error of adding remote: " << error->message;
462 g_variant_unref(options);
465 if (ostree_repo_remote_change(repo,
nullptr, OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS, remote, url.c_str(),
466 options, cancellable, &error) == 0) {
467 LOG_ERROR <<
"Error of adding remote: " << error->message;
469 g_variant_unref(options);
472 g_variant_unref(options);
Provides a thread-safe way to pause and terminate task execution.
bool IsOstree() const
Is this an OSTree target? OSTree targets need special treatment because the hash doesn't represent th...
Operation has already been processed.
Package installation failed.