Browse code

Reimplemented progress bar for freshclam downloads using libcurl API. Included basic time-remaining information as per user request, see: bb5943, though not in the specific format mentioned.

Micah Snyder (micasnyd) authored on 2019/07/09 05:28:39
Showing 1 changed files
... ...
@@ -63,6 +63,7 @@
63 63
 #include <dirent.h>
64 64
 #include <errno.h>
65 65
 #include <zlib.h>
66
+#include <math.h>
66 67
 
67 68
 #ifdef _WIN32
68 69
 #include <wincrypt.h>
... ...
@@ -226,6 +227,125 @@ done:
226 226
 }
227 227
 #endif
228 228
 
229
+#if LIBCURL_VERSION_NUM >= 0x073d00
230
+/* In libcurl 7.61.0, support was added for extracting the time in plain
231
+   microseconds. Older libcurl versions are stuck in using 'double' for this
232
+   information so we complicate this example a bit by supporting either
233
+   approach. */
234
+#define TIME_IN_US 1
235
+#define TIMETYPE curl_off_t
236
+#define TIMEOPT CURLINFO_TOTAL_TIME_T
237
+#define MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL 3000000
238
+#else
239
+#define TIMETYPE double
240
+#define TIMEOPT CURLINFO_TOTAL_TIME
241
+#define MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL 3
242
+#endif
243
+
244
+#define STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES 6000
245
+
246
+struct xfer_progress {
247
+    TIMETYPE lastRunTime; /* type depends on version, see above */
248
+    uint8_t bComplete;
249
+    CURL *curl;
250
+};
251
+
252
+/**
253
+ * Function from curl example code, Copyright (C) 1998 - 2018, Daniel Stenberg, see COPYING.curl for license details
254
+ * Progress bar callback function ( CURLOPT_XFERINFOFUNCTION ).
255
+ */
256
+static int xferinfo(void *prog,
257
+                    curl_off_t TotalToDownload, curl_off_t NowDownloaded,
258
+                    curl_off_t TotalToUpload, curl_off_t NowUploaded)
259
+{
260
+    struct xfer_progress *xferProg = (struct xfer_progress *)prog;
261
+    CURL *curl                     = xferProg->curl;
262
+    TIMETYPE curtime               = 0;
263
+    TIMETYPE remtime               = 0;
264
+
265
+    uint32_t i                = 0;
266
+    uint32_t totalNumDots     = 40;
267
+    uint32_t numDots          = 0;
268
+    double fractiondownloaded = 0.0;
269
+
270
+    if ((TotalToDownload <= 0.0) || (xferProg->bComplete)) {
271
+        return 0;
272
+    }
273
+
274
+    fractiondownloaded = (double)NowDownloaded / (double)TotalToDownload;
275
+    numDots            = round(fractiondownloaded * totalNumDots);
276
+
277
+    curl_easy_getinfo(curl, TIMEOPT, &curtime);
278
+
279
+    xferProg->lastRunTime = curtime;
280
+    remtime               = (curtime * 1 / fractiondownloaded) - curtime;
281
+
282
+#ifdef TIME_IN_US
283
+    if (fractiondownloaded <= 0.0) {
284
+        fprintf(stdout, "Elapsed: %" CURL_FORMAT_CURL_OFF_T ".%06ld sec. ",
285
+                (curtime / 1000000), (long)(curtime % 1000000));
286
+    } else {
287
+        fprintf(stdout, "Elapsed: %" CURL_FORMAT_CURL_OFF_T ".%06ld sec, Remaining; %f sec. ",
288
+                (curtime / 1000000), (long)(curtime % 1000000),
289
+                (remtime / 1000000), (long)(remtime % 1000000));
290
+    }
291
+#else
292
+    if (fractiondownloaded <= 0.0) {
293
+        fprintf(stdout, "Elapsed: %f sec. ", curtime);
294
+    } else {
295
+        fprintf(stdout, "Elapsed: %f sec, Remaining: %f sec. ", curtime, remtime);
296
+    }
297
+#endif
298
+
299
+    if (TotalToUpload > 0.0) {
300
+        fprintf(stdout, "Uploaded: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T,
301
+                NowUploaded, TotalToUpload);
302
+    } else if (TotalToDownload > 0.0) {
303
+        fprintf(stdout, "Downloaded: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T,
304
+                NowDownloaded, TotalToDownload);
305
+    }
306
+
307
+    fprintf(stdout, " [");
308
+    if (numDots > 0) {
309
+        if (numDots > 1) {
310
+            for (i = 0; i < numDots - 1; i++) {
311
+                fprintf(stdout, "=");
312
+            }
313
+            fprintf(stdout, ">");
314
+            i++;
315
+        }
316
+    }
317
+    for (; i < totalNumDots; i++) {
318
+        printf(" ");
319
+    }
320
+    if (NowDownloaded < TotalToDownload) {
321
+        fprintf(stdout, "]  \r");
322
+    } else {
323
+        fprintf(stdout, "]  \n");
324
+        xferProg->bComplete = 1;
325
+    }
326
+    fflush(stdout);
327
+
328
+    return 0;
329
+}
330
+
331
+#if LIBCURL_VERSION_NUM < 0x072000
332
+/**
333
+ * Function from curl example code, Copyright (C) 1998 - 2018, Daniel Stenberg, see COPYING.curl for license details
334
+ * Older style progress bar callback shim; for libcurl older than 7.32.0 ( CURLOPT_PROGRESSFUNCTION ).
335
+ */
336
+static int older_progress(void *prog,
337
+                          double TotalToDownload, double NowDownloaded,
338
+                          double TotalToUpload, double NowUploaded)
339
+{
340
+    return xferinfo(prog,
341
+                    (curl_off_t)TotalToDownload,
342
+                    (curl_off_t)NowDownloaded,
343
+                    (curl_off_t)TotalToUpload,
344
+                    (curl_off_t)NowUploaded);
345
+}
346
+#endif
347
+
229 348
 static fc_error_t create_curl_handle(
230 349
     int bHttp,
231 350
     int bAllowRedirect,
... ...
@@ -233,8 +353,11 @@ static fc_error_t create_curl_handle(
233 233
 {
234 234
     fc_error_t status = FC_EARG;
235 235
 
236
-    CURL *curl        = NULL;
236
+    CURL *curl = NULL;
237
+
238
+#if defined(CURLOPT_DNS_LOCAL_IP4) || defined(CURLOPT_DNS_LOCAL_IP4)
237 239
     CURLcode curl_ret = CURLE_OK;
240
+#endif
238 241
 
239 242
     char userAgent[128];
240 243
 
... ...
@@ -485,6 +608,7 @@ static fc_error_t remote_cvdhead(
485 485
     CURLcode curl_ret;
486 486
     char errbuf[CURL_ERROR_SIZE];
487 487
     struct curl_slist *slist = NULL;
488
+    struct xfer_progress prog;
488 489
 
489 490
     long http_code = 0;
490 491
 
... ...
@@ -519,6 +643,45 @@ static fc_error_t remote_cvdhead(
519 519
         goto done;
520 520
     }
521 521
 
522
+    if (mprintf_progress) {
523
+        prog.lastRunTime = 0;
524
+        prog.curl        = curl;
525
+        prog.bComplete   = 0;
526
+
527
+#if LIBCURL_VERSION_NUM >= 0x072000
528
+        /* xferinfo was introduced in 7.32.0, no earlier libcurl versions will
529
+       compile as they won't have the symbols around.
530
+
531
+       If built with a newer libcurl, but running with an older libcurl:
532
+       curl_easy_setopt() will fail in run-time trying to set the new
533
+       callback, making the older callback get used.
534
+
535
+       New libcurls will prefer the new callback and instead use that one even
536
+       if both callbacks are set. */
537
+
538
+        if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo)) {
539
+            logg("!create_curl_handle: Failed to set SSL CTX function!\n");
540
+        }
541
+        /* pass the struct pointer into the xferinfo function, note that this is
542
+       an alias to CURLOPT_PROGRESSDATA */
543
+        if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &prog)) {
544
+            logg("!create_curl_handle: Failed to set SSL CTX function!\n");
545
+        }
546
+#else
547
+        if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, older_progress)) {
548
+            logg("!create_curl_handle: Failed to set SSL CTX function!\n");
549
+        }
550
+        /* pass the struct pointer into the progress function */
551
+        if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &prog)) {
552
+            logg("!create_curl_handle: Failed to set SSL CTX function!\n");
553
+        }
554
+#endif
555
+
556
+        if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L)) {
557
+            logg("!create_curl_handle: Failed to set SSL CTX function!\n");
558
+        }
559
+    }
560
+
522 561
     if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_URL, url)) {
523 562
         logg("!remote_cvdhead: Failed to set CURLOPT_URL for curl session (%s).\n", url);
524 563
         status = FC_EFAILEDGET;
... ...
@@ -715,6 +878,7 @@ static fc_error_t downloadFile(
715 715
     CURLcode curl_ret;
716 716
     char errbuf[CURL_ERROR_SIZE];
717 717
     struct curl_slist *slist = NULL;
718
+    struct xfer_progress prog;
718 719
 
719 720
     long http_code = 0;
720 721
 
... ...
@@ -737,6 +901,45 @@ static fc_error_t downloadFile(
737 737
         goto done;
738 738
     }
739 739
 
740
+    if (mprintf_progress) {
741
+        prog.lastRunTime = 0;
742
+        prog.curl        = curl;
743
+        prog.bComplete   = 0;
744
+
745
+#if LIBCURL_VERSION_NUM >= 0x072000
746
+        /* xferinfo was introduced in 7.32.0, no earlier libcurl versions will
747
+       compile as they won't have the symbols around.
748
+
749
+       If built with a newer libcurl, but running with an older libcurl:
750
+       curl_easy_setopt() will fail in run-time trying to set the new
751
+       callback, making the older callback get used.
752
+
753
+       New libcurls will prefer the new callback and instead use that one even
754
+       if both callbacks are set. */
755
+
756
+        if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo)) {
757
+            logg("!create_curl_handle: Failed to set SSL CTX function!\n");
758
+        }
759
+        /* pass the struct pointer into the xferinfo function, note that this is
760
+       an alias to CURLOPT_PROGRESSDATA */
761
+        if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &prog)) {
762
+            logg("!create_curl_handle: Failed to set SSL CTX function!\n");
763
+        }
764
+#else
765
+        if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, older_progress)) {
766
+            logg("!create_curl_handle: Failed to set SSL CTX function!\n");
767
+        }
768
+        /* pass the struct pointer into the progress function */
769
+        if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &prog)) {
770
+            logg("!create_curl_handle: Failed to set SSL CTX function!\n");
771
+        }
772
+#endif
773
+
774
+        if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L)) {
775
+            logg("!create_curl_handle: Failed to set SSL CTX function!\n");
776
+        }
777
+    }
778
+
740 779
     if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_URL, url)) {
741 780
         logg("!downloadFile: Failed to set CURLOPT_URL for curl session (%s).\n", url);
742 781
     }
... ...
@@ -1761,11 +1964,22 @@ fc_error_t updatedb(
1761 1761
             goto done;
1762 1762
         }
1763 1763
 
1764
+        if (mprintf_progress) {
1765
+            if (remoteVersion - localVersion == 1) {
1766
+                mprintf("Current database is 1 version behind.\n");
1767
+            } else {
1768
+                mprintf("Current database is %u versions behind.\n", remoteVersion - localVersion);
1769
+            }
1770
+        }
1764 1771
         for (i = localVersion + 1; i <= remoteVersion; i++) {
1765 1772
             for (j = 1; j <= g_maxAttempts; j++) {
1766 1773
                 int llogerr = logerr;
1767 1774
                 if (logerr)
1768 1775
                     llogerr = (j == g_maxAttempts);
1776
+
1777
+                if (mprintf_progress) {
1778
+                    mprintf("Downloading database patch # %u...\n", i);
1779
+                }
1769 1780
                 ret = downloadPatch(database, tmpdir, i, server, llogerr);
1770 1781
                 if (ret == FC_ECONNECTION || ret == FC_EFAILEDGET) {
1771 1782
                     continue;
... ...
@@ -1846,7 +2060,7 @@ fc_error_t updatedb(
1846 1846
         }
1847 1847
     }
1848 1848
 
1849
-    /*
1849
+/*
1850 1850
      * Replace original database with new database.
1851 1851
      */
1852 1852
 #ifdef _WIN32