From ac68073ffce34558717cac5965bac90c89925fc2 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Wed, 20 Feb 2019 09:51:55 +0100 Subject: [PATCH] depot_download: support downloading index files With this commit, the 'installation' input of the depot-download subsystem accepts nodes in addition to nodes. Each index node refers to one index file specified via the 'path' attribute. This commit also improves the tracking of failure states. Once an installation job failed (due to a download of verification error), it won't get re-scheduled. In the past, such failure states were not kept across subsequent import iterations, which could result in infinite re-attempts when an installation contained archives from multiple users. The the progress of the download process is now reflected by the "progress" attribute on the download manager's state report, which allows the final report to contain the list of installed/failed archives along with the overall progress/completed state. The detection of the latter is important for the sculpt manager for reattempting the deployment of the completed packages. The patch enhances the depot_download.run script to stress the new abilities. In particular, the scenario downloads a mix of index files (one present, one missing) and archives, from two different depot users (genodelabs and nfeske). Issue #3172 --- repos/gems/include/depot/archive.h | 26 +++- .../raw/depot_download/depot_download.config | 3 + repos/gems/run/depot_download.run | 37 ++++-- .../depot_download_manager/gen_depot_query.cc | 40 +++++- .../app/depot_download_manager/gen_extract.cc | 12 +- .../depot_download_manager/gen_fetchurl.cc | 8 +- .../app/depot_download_manager/gen_verify.cc | 6 +- .../src/app/depot_download_manager/import.h | 85 ++++++++++--- .../gems/src/app/depot_download_manager/job.h | 65 ++++++++++ .../src/app/depot_download_manager/main.cc | 119 +++++++++++++++--- .../gems/src/app/depot_download_manager/xml.h | 4 +- repos/gems/src/app/sculpt_manager/main.cc | 18 ++- 12 files changed, 357 insertions(+), 66 deletions(-) create mode 100644 repos/gems/src/app/depot_download_manager/job.h diff --git a/repos/gems/include/depot/archive.h b/repos/gems/include/depot/archive.h index 8278beb5fe..f25fcd0fe9 100644 --- a/repos/gems/include/depot/archive.h +++ b/repos/gems/include/depot/archive.h @@ -88,8 +88,30 @@ struct Depot::Archive throw Unknown_archive_type(); } - static Name name (Path const &path) { return _path_element(path, 2); } - static Version version(Path const &path) { return _path_element(path, 3); } + /** + * Return true if 'path' refers to an index file + */ + static bool index(Path const &path) + { + return _path_element(path, 1) == "index"; + } + + static Name name (Path const &path) { return _path_element(path, 2); } + static Version version (Path const &path) { return _path_element(path, 3); } + static Version index_version(Path const &path) { return _path_element(path, 2); } + + /** + * Return name of compressed file to download for the given depot path + * + * Archives are shipped as tar.xz files whereas index files are shipped + * as xz-compressed files. + */ + static Archive::Path download_file_path(Archive::Path path) + { + return Archive::index(path) ? Archive::Path(path, ".xz") + : Archive::Path(path, ".tar.xz"); + } + }; #endif /* _INCLUDE__DEPOT__ARCHIVE_H_ */ diff --git a/repos/gems/recipes/raw/depot_download/depot_download.config b/repos/gems/recipes/raw/depot_download/depot_download.config index 21e2fe7a86..1d23799204 100644 --- a/repos/gems/recipes/raw/depot_download/depot_download.config +++ b/repos/gems/recipes/raw/depot_download/depot_download.config @@ -26,6 +26,8 @@ report="manager -> init_config"/> + + diff --git a/repos/gems/run/depot_download.run b/repos/gems/run/depot_download.run index cfddb8675d..e1cf90f8ca 100644 --- a/repos/gems/run/depot_download.run +++ b/repos/gems/run/depot_download.run @@ -51,11 +51,11 @@ set config { append_platform_drv_config -proc depot_user_download { } { - return [exec cat [genode_dir]/depot/[depot_user]/download] } +proc depot_user_download { user } { + return [exec cat [genode_dir]/depot/$user/download] } -proc depot_user_pubkey { } { - return [exec cat [genode_dir]/depot/[depot_user]/pubkey] } +proc depot_user_pubkey { user } { + return [exec cat [genode_dir]/depot/$user/pubkey] } append config { @@ -69,10 +69,15 @@ append config { - + - } [depot_user_download] { - } [depot_user_pubkey] { + } [depot_user_download nfeske] { + } [depot_user_pubkey nfeske] { + + + + } [depot_user_download genodelabs] { + } [depot_user_pubkey genodelabs] { @@ -109,10 +114,12 @@ append config { set fd [open [run_dir]/genode/installation w] -puts $fd " - - -" +puts $fd { + + + + +} close $fd @@ -129,5 +136,11 @@ build_boot_image $boot_modules append qemu_args " -nographic -net nic,model=e1000 -net user " -run_genode_until {.*\[init -> depot_download -> manager\] installation complete.*\n} 150 +# watch the state reports generated by the depot-download manager +set expected_pattern {} +append expected_pattern {.*path="genodelabs/pkg/wm/2018-02-26" state="done".*} +append expected_pattern {.*path="nfeske/index/19.02" state="done".*} +append expected_pattern {.*path="nfeske/index/19.03" state="failed".*} + +run_genode_until $expected_pattern 150 diff --git a/repos/gems/src/app/depot_download_manager/gen_depot_query.cc b/repos/gems/src/app/depot_download_manager/gen_depot_query.cc index dcea5474af..efe7e56b16 100644 --- a/repos/gems/src/app/depot_download_manager/gen_depot_query.cc +++ b/repos/gems/src/app/depot_download_manager/gen_depot_query.cc @@ -16,7 +16,8 @@ void Depot_download_manager::gen_depot_query_start_content(Xml_generator &xml, Xml_node installation, Archive::User const &next_user, - Depot_query_version version) + Depot_query_version version, + List_model const &jobs) { gen_common_start_content(xml, "depot_query", Cap_quota{100}, Ram_quota{2*1024*1024}); @@ -33,7 +34,28 @@ void Depot_download_manager::gen_depot_query_start_content(Xml_generator &xml, }); }); + /* + * Filter out failed parts of the installation from being re-queried. + * The inclusion of those parts may otherwise result in an infinite + * loop if the installation is downloaded from a mix of depot users. + */ + auto job_failed = [&] (Xml_node node) + { + Archive::Path const path = node.attribute_value("path", Archive::Path()); + + bool failed = false; + jobs.for_each([&] (Job const &job) { + if (job.path == path && job.failed) + failed = true; }); + + return failed; + }; + installation.for_each_sub_node("archive", [&] (Xml_node archive) { + + if (job_failed(archive)) + return; + xml.node("dependencies", [&] () { xml.attribute("path", archive.attribute_value("path", Archive::Path())); xml.attribute("source", archive.attribute_value("source", true)); @@ -41,6 +63,22 @@ void Depot_download_manager::gen_depot_query_start_content(Xml_generator &xml, }); }); + installation.for_each_sub_node("index", [&] (Xml_node index) { + + if (job_failed(index)) + return; + + xml.node("index", [&] () { + Archive::Path const path = index.attribute_value("path", Archive::Path()); + if (!Archive::index(path)) { + warning("malformed index path '", path, "'"); + return; + } + xml.attribute("user", Archive::user(path)); + xml.attribute("version", Archive::_path_element(path, 2)); + }); + }); + if (next_user.valid()) xml.node("user", [&] () { xml.attribute("name", next_user); }); }); diff --git a/repos/gems/src/app/depot_download_manager/gen_extract.cc b/repos/gems/src/app/depot_download_manager/gen_extract.cc index 82bd713666..2ea6504a87 100644 --- a/repos/gems/src/app/depot_download_manager/gen_extract.cc +++ b/repos/gems/src/app/depot_download_manager/gen_extract.cc @@ -54,15 +54,13 @@ void Depot_download_manager::gen_extract_start_content(Xml_generator &xml, import.for_each_verified_archive([&] (Archive::Path const &path) { typedef String<160> Path; - typedef String<16> Ext; - - Ext const ext (".tar.xz"); - Path const tar_path ("/public/", path, ext); - Path const dst_path ("/depot/", without_last_path_element(path)); xml.node("extract", [&] () { - xml.attribute("archive", tar_path); - xml.attribute("to", dst_path); + xml.attribute("archive", Path("/public/", Archive::download_file_path(path))); + xml.attribute("to", Path("/depot/", without_last_path_element(path))); + + if (Archive::index(path)) + xml.attribute("name", Archive::index_version(path)); }); }); }); diff --git a/repos/gems/src/app/depot_download_manager/gen_fetchurl.cc b/repos/gems/src/app/depot_download_manager/gen_fetchurl.cc index a7c4482b10..9608fddbd6 100644 --- a/repos/gems/src/app/depot_download_manager/gen_fetchurl.cc +++ b/repos/gems/src/app/depot_download_manager/gen_fetchurl.cc @@ -58,11 +58,11 @@ void Depot_download_manager::gen_fetchurl_start_content(Xml_generator &xml, import.for_each_download([&] (Archive::Path const &path) { typedef String<160> Remote; typedef String<160> Local; - typedef String<16> Ext; + typedef String<100> File_path; - Ext const ext (".tar.xz"); - Remote const remote (current_user_url, "/", path, ext); - Local const local ("/download/", path, ext); + File_path const file_path = Archive::download_file_path(path); + Remote const remote (current_user_url, "/", file_path); + Local const local ("/download/", file_path); xml.node("fetch", [&] () { xml.attribute("url", remote); diff --git a/repos/gems/src/app/depot_download_manager/gen_verify.cc b/repos/gems/src/app/depot_download_manager/gen_verify.cc index 1daefec6a5..f40c4f00b1 100644 --- a/repos/gems/src/app/depot_download_manager/gen_verify.cc +++ b/repos/gems/src/app/depot_download_manager/gen_verify.cc @@ -48,14 +48,12 @@ void Depot_download_manager::gen_verify_start_content(Xml_generator &xml, import.for_each_unverified_archive([&] (Archive::Path const &path) { typedef String<160> Path; - typedef String<16> Ext; - Ext const ext (".tar.xz"); - Path const tar_path ("/public/", path, ext); + Path const file_path ("/public/", Archive::download_file_path(path)); Path const pubkey_path (user_path, "/pubkey"); xml.node("verify", [&] () { - xml.attribute("path", tar_path); + xml.attribute("path", file_path); xml.attribute("pubkey", pubkey_path); }); }); diff --git a/repos/gems/src/app/depot_download_manager/import.h b/repos/gems/src/app/depot_download_manager/import.h index 160a7f4a25..0852aa5d42 100644 --- a/repos/gems/src/app/depot_download_manager/import.h +++ b/repos/gems/src/app/depot_download_manager/import.h @@ -41,6 +41,15 @@ class Depot_download_manager::Import { typedef String<32> Bytes; Bytes total, now; + + bool complete() const + { + /* fetchurl has not yet determined the file size */ + if (total == "0.0") + return false; + + return now == total; + } }; virtual Info download_progress(Archive::Path const &) const = 0; @@ -57,6 +66,7 @@ class Depot_download_manager::Import enum State { DOWNLOAD_IN_PROGRESS, DOWNLOAD_COMPLETE, DOWNLOAD_UNAVAILABLE, + VERIFICATION_IN_PROGRESS, VERIFIED, VERIFICATION_FAILED, UNPACKED }; @@ -71,12 +81,13 @@ class Depot_download_manager::Import char const *state_text() const { switch (state) { - case DOWNLOAD_IN_PROGRESS: return "download"; - case DOWNLOAD_COMPLETE: return "verify"; - case DOWNLOAD_UNAVAILABLE: return "unavailable"; - case VERIFIED: return "extract"; - case VERIFICATION_FAILED: return "verification failed"; - case UNPACKED: return "done"; + case DOWNLOAD_IN_PROGRESS: return "download"; + case DOWNLOAD_COMPLETE: return "fetched"; + case DOWNLOAD_UNAVAILABLE: return "unavailable"; + case VERIFICATION_IN_PROGRESS: return "verify"; + case VERIFIED: return "extract"; + case VERIFICATION_FAILED: return "corrupted"; + case UNPACKED: return "done"; }; return ""; } @@ -111,22 +122,35 @@ class Depot_download_manager::Import /** * Constructor * - * \param user depot origin to use for the import - * \param node XML node containing any number of '' sub nodes + * \param user depot origin to use for the import + * \param dependencies information about '' archives + * \param index information about '' index files * * The import constructor considers only those '' sub nodes as * items that match the 'user'. The remaining sub nodes are imported in * a future iteration. */ - Import(Allocator &alloc, Archive::User const &user, Xml_node node) + Import(Allocator &alloc, Archive::User const &user, + Xml_node dependencies, Xml_node index) : _alloc(alloc) { - node.for_each_sub_node("missing", [&] (Xml_node item) { + dependencies.for_each_sub_node("missing", [&] (Xml_node item) { Archive::Path const path = item.attribute_value("path", Archive::Path()); if (Archive::user(path) == user) new (alloc) Item(_items, path); }); + + index.for_each_sub_node("missing", [&] (Xml_node item) { + + Archive::Path const + path(item.attribute_value("user", Archive::User()), + "/index/", + item.attribute_value("version", Archive::Version())); + + if (Archive::user(path) == user) + new (alloc) Item(_items, path); + }); } ~Import() @@ -139,11 +163,16 @@ class Depot_download_manager::Import return _item_state_exists(Item::DOWNLOAD_IN_PROGRESS); } - bool unverified_archives_available() const + bool completed_downloads_available() const { return _item_state_exists(Item::DOWNLOAD_COMPLETE); } + bool unverified_archives_available() const + { + return _item_state_exists(Item::VERIFICATION_IN_PROGRESS); + } + bool verified_archives_available() const { return _item_state_exists(Item::VERIFIED); @@ -158,7 +187,7 @@ class Depot_download_manager::Import template void for_each_unverified_archive(FN const &fn) const { - _for_each_item(Item::DOWNLOAD_COMPLETE, fn); + _for_each_item(Item::VERIFICATION_IN_PROGRESS, fn); } template @@ -173,6 +202,13 @@ class Depot_download_manager::Import _for_each_item(Item::UNPACKED, fn); } + template + void for_each_failed_archive(FN const &fn) const + { + _for_each_item(Item::DOWNLOAD_UNAVAILABLE, fn); + _for_each_item(Item::VERIFICATION_FAILED, fn); + } + void all_downloads_completed() { _items.for_each([&] (Item &item) { @@ -180,7 +216,26 @@ class Depot_download_manager::Import item.state = Item::DOWNLOAD_COMPLETE; }); } - void all_downloads_unavailable() + void verify_all_downloaded_archives() + { + _items.for_each([&] (Item &item) { + if (item.state == Item::DOWNLOAD_COMPLETE) + item.state = Item::VERIFICATION_IN_PROGRESS; }); + } + + void apply_download_progress(Download_progress const &progress) + { + _items.for_each([&] (Item &item) { + + if (item.state == Item::DOWNLOAD_IN_PROGRESS + && progress.download_progress(item.path).complete()) { + + item.state = Item::DOWNLOAD_COMPLETE; + } + }); + } + + void all_remaining_downloads_unavailable() { _items.for_each([&] (Item &item) { if (item.state == Item::DOWNLOAD_IN_PROGRESS) @@ -190,7 +245,7 @@ class Depot_download_manager::Import void archive_verified(Archive::Path const &archive) { _items.for_each([&] (Item &item) { - if (item.state == Item::DOWNLOAD_COMPLETE) + if (item.state == Item::VERIFICATION_IN_PROGRESS) if (item.path == archive) item.state = Item::VERIFIED; }); } @@ -198,7 +253,7 @@ class Depot_download_manager::Import void archive_verification_failed(Archive::Path const &archive) { _items.for_each([&] (Item &item) { - if (item.state == Item::DOWNLOAD_COMPLETE) + if (item.state == Item::VERIFICATION_IN_PROGRESS) if (item.path == archive) item.state = Item::VERIFICATION_FAILED; }); } diff --git a/repos/gems/src/app/depot_download_manager/job.h b/repos/gems/src/app/depot_download_manager/job.h new file mode 100644 index 0000000000..2bda7d64e5 --- /dev/null +++ b/repos/gems/src/app/depot_download_manager/job.h @@ -0,0 +1,65 @@ +/* + * \brief Failure state of jobs submitted via the 'installation' + * \author Norman Feske + * \date 2019-02-21 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _JOB_H_ +#define _JOB_H_ + +/* Genode includes */ +#include +#include + +#include "types.h" + +namespace Depot_download_manager { + using namespace Depot; + struct Job; +} + + +struct Depot_download_manager::Job : List_model::Element +{ + bool started = false; + bool failed = false; + + Archive::Path const path; + + Job(Archive::Path const &path) : path(path) { } + + struct Update_policy + { + typedef Job Element; + + Allocator &_alloc; + + Update_policy(Allocator &alloc) : _alloc(alloc) { } + + void destroy_element(Job &elem) { destroy(_alloc, &elem); } + + Job &create_element(Xml_node elem_node) + { + return *new (_alloc) + Job(elem_node.attribute_value("path", Archive::Path())); + } + + void update_element(Job &, Xml_node) { } + + static bool element_matches_xml_node(Job const &job, Xml_node node) + { + return node.attribute_value("path", Archive::Path()) == job.path; + } + + static bool node_is_element(Xml_node) { return true; } + }; +}; + +#endif /* _JOB_H_ */ diff --git a/repos/gems/src/app/depot_download_manager/main.cc b/repos/gems/src/app/depot_download_manager/main.cc index d65a9a5697..40c8629ddb 100644 --- a/repos/gems/src/app/depot_download_manager/main.cc +++ b/repos/gems/src/app/depot_download_manager/main.cc @@ -58,6 +58,7 @@ struct Depot_download_manager::Main : Import::Download_progress Attached_rom_dataspace _installation { _env, "installation" }; Attached_rom_dataspace _dependencies { _env, "dependencies" }; + Attached_rom_dataspace _index { _env, "index" }; Attached_rom_dataspace _init_state { _env, "init_state" }; Attached_rom_dataspace _fetchurl_progress { _env, "fetchurl_progress" }; @@ -104,6 +105,8 @@ struct Depot_download_manager::Main : Import::Download_progress Archive::User _next_user { }; + List_model _jobs { }; + Constructible _import { }; /** @@ -113,7 +116,7 @@ struct Depot_download_manager::Main : Import::Download_progress { Info result { Info::Bytes(), Info::Bytes() }; try { - Url const url_path(_current_user_url(), "/", path, ".tar.xz"); + Url const url_path(_current_user_url(), "/", Archive::download_file_path(path)); /* search fetchurl progress report for matching 'url_path' */ _fetchurl_progress.xml().for_each_sub_node("fetch", [&] (Xml_node fetch) { @@ -128,8 +131,32 @@ struct Depot_download_manager::Main : Import::Download_progress void _update_state_report() { _state_reporter.generate([&] (Xml_generator &xml) { - if (_import.constructed()) - _import->report(xml, *this); }); + + /* produce detailed reports while the installation is in progress */ + if (_import.constructed()) { + xml.attribute("progress", "yes"); + _import->report(xml, *this); + } + + /* once all imports have settled, present the final results */ + else { + _jobs.for_each([&] (Job const &job) { + + if (!job.started) + return; + + /* + * If a job has been started and has not failed, it must + * have succeeded at the time when the import is finished. + */ + char const *type = Archive::index(job.path) ? "index" : "archive"; + xml.node(type, [&] () { + xml.attribute("path", job.path); + xml.attribute("state", job.failed ? "failed" : "done"); + }); + }); + } + }); } void _generate_init_config(Xml_generator &); @@ -143,6 +170,10 @@ struct Depot_download_manager::Main : Import::Download_progress void _handle_installation() { _installation.update(); + + Job::Update_policy policy(_heap); + _jobs.update_from_xml(policy, _installation.xml()); + _generate_init_config(); } @@ -165,18 +196,29 @@ struct Depot_download_manager::Main : Import::Download_progress void _handle_fetchurl_progress() { _fetchurl_progress.update(); + + if (_import.constructed()) { + _import->apply_download_progress(*this); + + /* proceed with next import step if all downloads are done or failed */ + if (!_import->downloads_in_progress()) + _generate_init_config(); + } + _update_state_report(); } Main(Env &env) : _env(env) { _dependencies .sigh(_query_result_handler); + _index .sigh(_query_result_handler); _current_user .sigh(_query_result_handler); _init_state .sigh(_init_state_handler); _verified .sigh(_init_state_handler); _installation .sigh(_installation_handler); _fetchurl_progress.sigh(_fetchurl_progress_handler); + _handle_installation(); _generate_init_config(); } }; @@ -223,7 +265,7 @@ void Depot_download_manager::Main::_generate_init_config(Xml_generator &xml) xml.node("start", [&] () { gen_depot_query_start_content(xml, _installation.xml(), - _next_user, _depot_query_count); }); + _next_user, _depot_query_count, _jobs); }); if (_import.constructed() && _import->downloads_in_progress()) { try { @@ -259,23 +301,54 @@ void Depot_download_manager::Main::_handle_query_result() return; _dependencies.update(); + _index.update(); _current_user.update(); Xml_node const dependencies = _dependencies.xml(); - if (dependencies.num_sub_nodes() == 0) + Xml_node const index = _index.xml(); + + if (dependencies.num_sub_nodes() == 0 && index.num_sub_nodes() == 0) return; - if (!dependencies.has_sub_node("missing")) { + bool const missing_dependencies = dependencies.has_sub_node("missing"); + bool const missing_index_files = index.has_sub_node("missing"); + + if (!missing_dependencies && !missing_index_files) { log("installation complete."); _update_state_report(); return; } - Archive::Path const path = - dependencies.sub_node("missing").attribute_value("path", Archive::Path()); + /** + * Select depot user for next import + * + * Prefer the downloading of index files over archives because index files + * are quick to download and important for interactivity. + */ + auto select_next_user = [&] () + { + Archive::User user { }; - if (Archive::user(path) != _current_user_name()) { - _next_user = Archive::user(path); + if (missing_index_files) + index.with_sub_node("missing", [&] (Xml_node missing) { + user = missing.attribute_value("user", Archive::User()); }); + + if (user.valid()) + return user; + + dependencies.with_sub_node("missing", [&] (Xml_node missing) { + user = Archive::user(missing.attribute_value("path", Archive::Path())); }); + + if (!user.valid()) + warning("unable to select depot user for next import"); + + return user; + }; + + Archive::User const next_user = select_next_user(); + + if (next_user != _current_user_name()) { + _next_user = next_user; /* query user info from 'depot_query' */ _generate_init_config(); @@ -283,7 +356,14 @@ void Depot_download_manager::Main::_handle_query_result() } /* start new import */ - _import.construct(_heap, _current_user_name(), _dependencies.xml()); + _import.construct(_heap, _current_user_name(), dependencies, index); + + /* mark imported jobs as started */ + _import->for_each_download([&] (Archive::Path const &path) { + _jobs.for_each([&] (Job &job) { + if (job.path == path) + job.started = true; }); }); + _fetchurl_attempt = 0; _update_state_report(); @@ -316,7 +396,7 @@ void Depot_download_manager::Main::_handle_init_state() _fetchurl_count.value++; if (_fetchurl_attempt++ >= _fetchurl_max_attempts) { - import.all_downloads_unavailable(); + import.all_remaining_downloads_unavailable(); _fetchurl_attempt = 0; } @@ -326,11 +406,16 @@ void Depot_download_manager::Main::_handle_init_state() if (fetchurl_state.exited && fetchurl_state.code == 0) { import.all_downloads_completed(); - /* kill fetchurl, start untar */ + /* kill fetchurl, start verify */ reconfigure_init = true; } } + if (!import.downloads_in_progress() && import.completed_downloads_available()) { + import.verify_all_downloaded_archives(); + reconfigure_init = true; + } + if (import.unverified_archives_available()) { _verified.xml().for_each_sub_node([&] (Xml_node node) { @@ -341,7 +426,7 @@ void Depot_download_manager::Main::_handle_init_state() /* determine matching archive path */ Path path; import.for_each_unverified_archive([&] (Archive::Path const &archive) { - if (abs_path == Path("/public/", archive, ".tar.xz")) + if (abs_path == Path("/public/", Archive::download_file_path(archive))) path = archive; }); if (path.valid()) { @@ -375,6 +460,12 @@ void Depot_download_manager::Main::_handle_init_state() } } + /* flag failed jobs to prevent re-attempts in subsequent import iterations */ + import.for_each_failed_archive([&] (Archive::Path const &path) { + _jobs.for_each([&] (Job &job) { + if (job.path == path) + job.failed = true; }); }); + /* report before destructing '_import' to avoid empty intermediate reports */ if (reconfigure_init) _update_state_report(); diff --git a/repos/gems/src/app/depot_download_manager/xml.h b/repos/gems/src/app/depot_download_manager/xml.h index 5a6e39cb18..2504f5d8e8 100644 --- a/repos/gems/src/app/depot_download_manager/xml.h +++ b/repos/gems/src/app/depot_download_manager/xml.h @@ -27,6 +27,7 @@ /* local includes */ #include "import.h" +#include "job.h" namespace Depot_download_manager { @@ -84,7 +85,8 @@ namespace Depot_download_manager { void gen_depot_query_start_content(Xml_generator &, Xml_node installation, Archive::User const &, - Depot_query_version); + Depot_query_version, + List_model const &); void gen_fetchurl_start_content(Xml_generator &, Import const &, Url const &, Fetchurl_version); diff --git a/repos/gems/src/app/sculpt_manager/main.cc b/repos/gems/src/app/sculpt_manager/main.cc index d555ebfd7d..51a988df40 100644 --- a/repos/gems/src/app/sculpt_manager/main.cc +++ b/repos/gems/src/app/sculpt_manager/main.cc @@ -173,10 +173,16 @@ struct Sculpt::Main : Input_event_handler, Signal_handler
_update_state_handler { _env.ep(), *this, &Main::_handle_update_state }; - bool _update_running() const { return _storage._sculpt_partition.valid() - && !_prepare_in_progress() - && _network.ready() - && _deploy.update_needed(); }; + /** + * Condition for spawning the update subsystem + */ + bool _update_running() const + { + return _storage._sculpt_partition.valid() + && !_prepare_in_progress() + && _network.ready() + && _deploy.update_needed(); + } /************ @@ -281,7 +287,7 @@ struct Sculpt::Main : Input_event_handler, _deploy.gen_child_diagnostics(xml); Xml_node const state = _update_state_rom.xml(); - if (_update_running() && state.has_sub_node("archive")) + if (_update_running() && state.attribute_value("progress", false)) gen_download_status(xml, state); }); }); @@ -809,7 +815,7 @@ void Sculpt::Main::_handle_update_state() generate_dialog(); bool const installation_complete = - !_update_state_rom.xml().has_sub_node("archive"); + !_update_state_rom.xml().attribute_value("progress", false); if (installation_complete) _deploy.reattempt_after_installation();