[pacman-dev] [PATCH v2] Extend download callback interface with start/complete events

Anatol Pomozov anatol.pomozov at gmail.com
Wed May 6 02:50:51 UTC 2020


With the previous download interface the callback uses the first progress
event as 'download has started' signal. Unfortunately it does not work with
up-to-date files that never receive 'download progress' events.
Up-to-date database messages are currently handled in sync_syncdbs()
after the sequential download is completed and a result from ALPM is
received. But this is not going to work with multiplexed download
interface that returns the result only after all files are completed.

Another problem with 'first progress event is the beginning of the
download' is that such events time are unpredictable. Thus the UI progress
bar order might differ from what has been passed by client to
alpm_dbs_update() function. We actually want to keep the dbs progress bars
in a strict order.

To help to solve the given problems extend the download callback to
allow 2 more events - download started and completed. 'Download started'
events appear in the same order as in the list given by a client.

Signed-off-by: Anatol Pomozov <anatol.pomozov at gmail.com>
---
 lib/libalpm/alpm.h    | 35 ++++++++++++++++++++++++++++++++---
 lib/libalpm/dload.c   | 19 +++++++++++++++----
 src/pacman/callback.c | 10 +++++++++-
 src/pacman/callback.h |  3 ++-
 src/pacman/conf.c     |  2 +-
 5 files changed, 59 insertions(+), 10 deletions(-)

diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
index a31f7a8a..534a8189 100644
--- a/lib/libalpm/alpm.h
+++ b/lib/libalpm/alpm.h
@@ -715,13 +715,42 @@ typedef void (*alpm_cb_progress)(alpm_progress_t, const char *, int, size_t, siz
  * Downloading
  */
 
+/* File download events.
+ * These events are reported by ALPM via download callback.
+ */
+typedef enum {
+	ALPM_DOWNLOAD_INIT, /* alpm initializes file download logic */
+	ALPM_DOWNLOAD_PROGRESS, /* download progress reported */
+	ALPM_DOWNLOAD_COMPLETED /* alpm is about to release data related to the file */
+} alpm_download_event_type_t;
+
+typedef struct {
+	int optional; /* whether this file is optional and thus the errors could be ignored */
+} alpm_download_event_init_t;
+
+typedef struct {
+	off_t downloaded; /* amount of data downloaded */
+	off_t total; /* total amount need to be downloaded */
+} alpm_download_event_progress_t;
+
+typedef struct {
+	/* total bytes in file */
+	off_t total;
+	/* download result code:
+	 *   0 - download completed successfully
+	 *   1 - the file is up-to-date
+	 *   negative - error code
+	 */
+	int result;
+} alpm_download_event_completed_t;
+
 /** Type of download progress callbacks.
  * @param filename the name of the file being downloaded
- * @param xfered the number of transferred bytes
- * @param total the total number of bytes to transfer
+ * @param event the event type
+ * @param data the event data of type alpm_download_event_*_t
  */
 typedef void (*alpm_cb_download)(const char *filename,
-		off_t xfered, off_t total);
+		alpm_download_event_type_t event, void *data);
 
 typedef void (*alpm_cb_totaldl)(off_t total);
 
diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c
index c65bb5e4..2f360ab8 100644
--- a/lib/libalpm/dload.c
+++ b/lib/libalpm/dload.c
@@ -88,6 +88,7 @@ static int dload_progress_cb(void *file, curl_off_t dltotal, curl_off_t dlnow,
 {
 	struct dload_payload *payload = (struct dload_payload *)file;
 	off_t current_size, total_size;
+	alpm_download_event_progress_t cb_data = {0};
 
 	/* avoid displaying progress bar for redirects with a body */
 	if(payload->respcode >= 300) {
@@ -127,16 +128,20 @@ static int dload_progress_cb(void *file, curl_off_t dltotal, curl_off_t dlnow,
 	 * x {x>0}, x: download complete
 	 * x {x>0, x<y}, y {y > 0}: download progress, expected total is known */
 	if(!payload->cb_initialized) {
-		payload->handle->dlcb(payload->remote_name, 0, -1);
+		cb_data.downloaded = 0;
+		cb_data.total = -1;
 		payload->cb_initialized = 1;
 	}
 	if(payload->prevprogress == current_size) {
-		payload->handle->dlcb(payload->remote_name, 0, 0);
+		cb_data.downloaded = 0;
+		cb_data.total = 0;
 	} else {
 	/* do NOT include initial_size since it wasn't part of the package's
 	 * download_size (nor included in the total download size callback) */
-		payload->handle->dlcb(payload->remote_name, dlnow, dltotal);
+		cb_data.downloaded = dlnow;
+		cb_data.total = dltotal;
 	}
+	payload->handle->dlcb(payload->remote_name, ALPM_DOWNLOAD_PROGRESS, &cb_data);
 
 	payload->prevprogress = current_size;
 
@@ -660,6 +665,7 @@ static int curl_multi_check_finished_download(CURLM *curlm, CURLMsg *msg,
 	long remote_time = -1;
 	struct stat st;
 	char hostname[HOSTNAME_SIZE];
+	alpm_download_event_completed_t cb_data = {0};
 	int ret = -1;
 
 	curlerr = curl_easy_getinfo(curl, CURLINFO_PRIVATE, &payload);
@@ -819,7 +825,9 @@ cleanup:
 		unlink(payload->tempfile_name);
 	}
 
-	// TODO: report that the download has been completed
+	cb_data.total = bytes_dl;
+	cb_data.result = ret;
+	handle->dlcb(payload->remote_name, ALPM_DOWNLOAD_COMPLETED, &cb_data);
 
 	curl_multi_remove_handle(curlm, curl);
 	curl_easy_cleanup(curl);
@@ -937,6 +945,9 @@ static int curl_multi_download_internal(alpm_handle_t *handle,
 			struct dload_payload *payload = payloads->data;
 
 			if(curl_multi_add_payload(handle, curlm, payload, localpath) == 0) {
+				alpm_download_event_init_t cb_data = {.optional = payload->errors_ok};
+
+				handle->dlcb(payload->remote_name, ALPM_DOWNLOAD_INIT, &cb_data);
 				payloads = payloads->next;
 				// TODO: report that download has started
 			} else {
diff --git a/src/pacman/callback.c b/src/pacman/callback.c
index a129758b..613d59d4 100644
--- a/src/pacman/callback.c
+++ b/src/pacman/callback.c
@@ -648,7 +648,7 @@ void cb_dl_total(off_t total)
 }
 
 /* callback to handle display of download progress */
-void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total)
+static void dload_progress_event(const char *filename, off_t file_xfered, off_t file_total)
 {
 	static double rate_last;
 	static off_t xfered_last;
@@ -858,6 +858,14 @@ void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total)
 	return;
 }
 
+void cb_download(const char *filename, alpm_download_event_type_t event, void *data)
+{
+	if(event == ALPM_DOWNLOAD_PROGRESS) {
+		alpm_download_event_progress_t *progress = data;
+		dload_progress_event(filename, progress->downloaded, progress->total);
+	}
+}
+
 /* Callback to handle notifications from the library */
 void cb_log(alpm_loglevel_t level, const char *fmt, va_list args)
 {
diff --git a/src/pacman/callback.h b/src/pacman/callback.h
index b4bde5f2..6d92e86b 100644
--- a/src/pacman/callback.h
+++ b/src/pacman/callback.h
@@ -37,7 +37,8 @@ void cb_progress(alpm_progress_t event, const char *pkgname, int percent,
 /* callback to handle receipt of total download value */
 void cb_dl_total(off_t total);
 /* callback to handle display of download progress */
-void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total);
+void cb_download(const char *filename, alpm_download_event_type_t event,
+		void *data);
 
 /* callback to handle messages/notifications from pacman library */
 __attribute__((format(printf, 2, 0)))
diff --git a/src/pacman/conf.c b/src/pacman/conf.c
index 7390d741..ac5a5329 100644
--- a/src/pacman/conf.c
+++ b/src/pacman/conf.c
@@ -837,7 +837,7 @@ static int setup_libalpm(void)
 	config->handle = handle;
 
 	alpm_option_set_logcb(handle, cb_log);
-	alpm_option_set_dlcb(handle, cb_dl_progress);
+	alpm_option_set_dlcb(handle, cb_download);
 	alpm_option_set_eventcb(handle, cb_event);
 	alpm_option_set_questioncb(handle, cb_question);
 	alpm_option_set_progresscb(handle, cb_progress);
-- 
2.26.2


More information about the pacman-dev mailing list