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();