diff --git a/repos/gems/recipes/raw/depot_download/depot_download.config b/repos/gems/recipes/raw/depot_download/depot_download.config index 0d01fd5e54..1c1c5a9dd5 100644 --- a/repos/gems/recipes/raw/depot_download/depot_download.config +++ b/repos/gems/recipes/raw/depot_download/depot_download.config @@ -32,6 +32,8 @@ report="dynamic -> state"/> + @@ -81,11 +83,13 @@ + - - - - + + + + + @@ -95,7 +99,6 @@ - diff --git a/repos/gems/run/depot_download.run b/repos/gems/run/depot_download.run index 54afec5334..8d3e40a7f1 100644 --- a/repos/gems/run/depot_download.run +++ b/repos/gems/run/depot_download.run @@ -102,6 +102,7 @@ append config { + diff --git a/repos/gems/src/app/depot_download_manager/import.h b/repos/gems/src/app/depot_download_manager/import.h index 5a9895c192..d1fbeae19e 100644 --- a/repos/gems/src/app/depot_download_manager/import.h +++ b/repos/gems/src/app/depot_download_manager/import.h @@ -16,6 +16,7 @@ /* Genode includes */ #include +#include #include #include @@ -29,6 +30,22 @@ namespace Depot_download_manager { class Depot_download_manager::Import { + public: + + /** + * Interface for obtaining the download progress for a given archive + */ + struct Download_progress : Interface + { + struct Info + { + typedef String<32> Bytes; + Bytes total, now; + }; + + virtual Info download_progress(Archive::Path const &) const = 0; + }; + private: struct Item @@ -49,6 +66,18 @@ class Depot_download_manager::Import : _element(registry, *this), path(path) { } + + char const *state_text() const + { + switch (state) { + case DOWNLOAD_IN_PROGRESS: return "download"; + case DOWNLOAD_COMPLETE: return "verify"; + case VERIFIED: return "extract"; + case VERIFICATION_FAILED: return "verification failed"; + case UNPACKED: return "done"; + }; + return ""; + } }; Allocator &_alloc; @@ -171,6 +200,23 @@ class Depot_download_manager::Import if (item.state == Item::VERIFIED) item.state = Item::UNPACKED; }); } + + void report(Xml_generator &xml, Download_progress const &progress) const + { + _items.for_each([&] (Item const &item) { + xml.node("archive", [&] () { + xml.attribute("path", item.path); + xml.attribute("state", item.state_text()); + + if (item.state == Item::DOWNLOAD_IN_PROGRESS) { + Download_progress::Info const info = + progress.download_progress(item.path); + xml.attribute("total", info.total); + xml.attribute("now", info.now); + } + }); + }); + } }; #endif /* _IMPORT_H_ */ diff --git a/repos/gems/src/app/depot_download_manager/main.cc b/repos/gems/src/app/depot_download_manager/main.cc index ecdbb1fc57..d9aec3ed1d 100644 --- a/repos/gems/src/app/depot_download_manager/main.cc +++ b/repos/gems/src/app/depot_download_manager/main.cc @@ -50,15 +50,16 @@ struct Depot_download_manager::Child_exit_state }; -struct Depot_download_manager::Main +struct Depot_download_manager::Main : Import::Download_progress { Env &_env; Heap _heap { _env.ram(), _env.rm() }; - Attached_rom_dataspace _installation { _env, "installation" }; - Attached_rom_dataspace _dependencies { _env, "dependencies" }; - Attached_rom_dataspace _init_state { _env, "init_state" }; + Attached_rom_dataspace _installation { _env, "installation" }; + Attached_rom_dataspace _dependencies { _env, "dependencies" }; + Attached_rom_dataspace _init_state { _env, "init_state" }; + Attached_rom_dataspace _fetchurl_progress { _env, "fetchurl_progress" }; /** * User identity, from which current downloads are fetched @@ -89,6 +90,8 @@ struct Depot_download_manager::Main Expanding_reporter _init_config { _env, "config", "init_config" }; + Expanding_reporter _state_reporter { _env, "state", "state" }; + /** * Version counters, used to enforce the restart or reconfiguration of * components. @@ -100,6 +103,32 @@ struct Depot_download_manager::Main Constructible _import { }; + /** + * Download_progress interface + */ + Info download_progress(Archive::Path const &path) const + { + Info result { Info::Bytes(), Info::Bytes() }; + try { + Url const url_path(_current_user_url(), "/", path, ".tar.xz"); + + /* search fetchurl progress report for matching 'url_path' */ + _fetchurl_progress.xml().for_each_sub_node("fetch", [&] (Xml_node fetch) { + if (fetch.attribute_value("url", Url()) == url_path) + result = { .total = fetch.attribute_value("total", Info::Bytes()), + .now = fetch.attribute_value("now", Info::Bytes()) }; }); + + } catch (Invalid_download_url) { } + return result; + } + + void _update_state_report() + { + _state_reporter.generate([&] (Xml_generator &xml) { + if (_import.constructed()) + _import->report(xml, *this); }); + } + void _generate_init_config(Xml_generator &); void _generate_init_config() @@ -127,13 +156,23 @@ struct Depot_download_manager::Main void _handle_init_state(); + Signal_handler
_fetchurl_progress_handler { + _env.ep(), *this, &Main::_handle_fetchurl_progress }; + + void _handle_fetchurl_progress() + { + _fetchurl_progress.update(); + _update_state_report(); + } + Main(Env &env) : _env(env) { - _dependencies.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); + _dependencies .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); _generate_init_config(); } @@ -225,6 +264,7 @@ void Depot_download_manager::Main::_handle_query_result() if (!dependencies.has_sub_node("missing")) { log("installation complete."); + _update_state_report(); return; } @@ -241,6 +281,7 @@ void Depot_download_manager::Main::_handle_query_result() /* start new import */ _import.construct(_heap, _current_user_name(), _dependencies.xml()); + _update_state_report(); /* spawn fetchurl */ _generate_init_config(); @@ -324,6 +365,10 @@ void Depot_download_manager::Main::_handle_init_state() } } + /* report before destructing '_import' to avoid empty intermediate reports */ + if (reconfigure_init) + _update_state_report(); + if (import_finished) _import.destruct();