gboolean lr_fastestmirror_sort_internalmirrorlist(LrHandle *handle, GError **err) { assert(!err || *err == NULL); GSList *list = g_slist_prepend(NULL, handle); gboolean ret = lr_fastestmirror_sort_internalmirrorlists(list, err); g_slist_free(list); return ret; }
gboolean lr_download_packages(GSList *targets, LrPackageDownloadFlag flags, GError **err) { gboolean ret; gboolean failfast = flags & LR_PACKAGEDOWNLOAD_FAILFAST; struct sigaction old_sigact; GSList *downloadtargets = NULL; gboolean interruptible = FALSE; assert(!err || *err == NULL); if (!targets) return TRUE; // Check targets for (GSList *elem = targets; elem; elem = g_slist_next(elem)) { LrPackageTarget *packagetarget = elem->data; if (!packagetarget->handle) { continue; /* g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_BADFUNCARG, "Package target %s doesn't have specified a handle", packagetarget->relative_url); return FALSE; */ } if (packagetarget->handle->interruptible) interruptible = TRUE; // Check repotype // Note: Checked because lr_handle_prepare_internal_mirrorlist // support only LR_YUMREPO yet if (packagetarget->handle->repotype != LR_YUMREPO) { g_debug("%s: Bad repo type", __func__); g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_BADFUNCARG, "Bad repo type"); return FALSE; } } // Setup sighandler if (interruptible) { struct sigaction sigact; g_debug("%s: Using own SIGINT handler", __func__); memset(&sigact, 0, sizeof(old_sigact)); memset(&sigact, 0, sizeof(sigact)); sigemptyset(&sigact.sa_mask); sigact.sa_handler = lr_sigint_handler; sigaddset(&sigact.sa_mask, SIGINT); sigact.sa_flags = SA_RESTART; if (sigaction(SIGINT, &sigact, &old_sigact) == -1) { g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_SIGACTION, "Cannot set Librepo SIGINT handler"); return FALSE; } } // List of handles for fastest mirror resolving GSList *fmr_handles = NULL; // Prepare targets for (GSList *elem = targets; elem; elem = g_slist_next(elem)) { gchar *local_path; LrPackageTarget *packagetarget = elem->data; LrDownloadTarget *downloadtarget; gint64 realsize = -1; gboolean doresume = packagetarget->resume; // Reset output attributes of the handle lr_packagetarget_reset(packagetarget); // Prepare destination filename if (packagetarget->dest) { if (g_file_test(packagetarget->dest, G_FILE_TEST_IS_DIR)) { // Dir specified gchar *file_basename = g_path_get_basename(packagetarget->relative_url); local_path = g_build_filename(packagetarget->dest, file_basename, NULL); g_free(file_basename); } else { local_path = g_strdup(packagetarget->dest); } } else { // No destination path specified local_path = g_path_get_basename(packagetarget->relative_url); } packagetarget->local_path = g_string_chunk_insert(packagetarget->chunk, local_path); g_free(local_path); // Check expected size and real size if the file exists if (doresume && g_access(packagetarget->local_path, R_OK) == 0 && packagetarget->expectedsize > 0) { struct stat buf; if (stat(packagetarget->local_path, &buf)) { g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_IO, "Cannot stat %s: %s", packagetarget->local_path, g_strerror(errno)); return FALSE; } realsize = buf.st_size; if (packagetarget->expectedsize < realsize) // Existing file is bigger then the one that is expected, // disable resuming doresume = FALSE; } if (g_access(packagetarget->local_path, R_OK) == 0 && packagetarget->checksum && packagetarget->checksum_type != LR_CHECKSUM_UNKNOWN) { /* If the file exists and checksum is ok, then is pointless to * download the file again. * Moreover, if the resume is enabled and the file is already * completely downloaded, then the download is going to fail. */ int fd_r = open(packagetarget->local_path, O_RDONLY); if (fd_r != -1) { gboolean matches; ret = lr_checksum_fd_cmp(packagetarget->checksum_type, fd_r, packagetarget->checksum, 1, &matches, NULL); close(fd_r); if (ret && matches) { // Checksum calculation was ok and checksum matches g_debug("%s: Package %s is already downloaded (checksum matches)", __func__, packagetarget->local_path); packagetarget->err = g_string_chunk_insert( packagetarget->chunk, "Already downloaded"); // Call end callback LrEndCb end_cb = packagetarget->endcb; if (end_cb) end_cb(packagetarget->cbdata, LR_TRANSFER_ALREADYEXISTS, "Already downloaded"); continue; } else if (ret) { // Checksum calculation was ok but checksum doesn't match if (realsize != -1 && realsize == packagetarget->expectedsize) // File size is the same as the expected one // Don't try to resume doresume = FALSE; } } } if (doresume && realsize != -1 && realsize == packagetarget->expectedsize) { // File's size matches the expected one, the resume is enabled and // no checksum is known => expect that the file is // the one the user wants g_debug("%s: Package %s is already downloaded (size matches)", __func__, packagetarget->local_path); packagetarget->err = g_string_chunk_insert( packagetarget->chunk, "Already downloaded"); // Call end callback LrEndCb end_cb = packagetarget->endcb; if (end_cb) end_cb(packagetarget->cbdata, LR_TRANSFER_ALREADYEXISTS, "Already downloaded"); continue; } if (packagetarget->handle) { ret = lr_handle_prepare_internal_mirrorlist(packagetarget->handle, FALSE, err); if (!ret) goto cleanup; if (packagetarget->handle->fastestmirror) { if (!g_slist_find(fmr_handles, packagetarget->handle)) fmr_handles = g_slist_prepend(fmr_handles, packagetarget->handle); } } GSList *checksums = NULL; LrDownloadTargetChecksum *checksum; checksum = lr_downloadtargetchecksum_new(packagetarget->checksum_type, packagetarget->checksum); checksums = g_slist_prepend(checksums, checksum); downloadtarget = lr_downloadtarget_new(packagetarget->handle, packagetarget->relative_url, packagetarget->base_url, -1, packagetarget->local_path, checksums, packagetarget->expectedsize, doresume, packagetarget->progresscb, packagetarget->cbdata, packagetarget->endcb, packagetarget->mirrorfailurecb, packagetarget, packagetarget->byterangestart, packagetarget->byterangeend); downloadtargets = g_slist_append(downloadtargets, downloadtarget); } // Do Fastest Mirror resolving for all handles in one shot if (fmr_handles) { fmr_handles = g_slist_reverse(fmr_handles); ret = lr_fastestmirror_sort_internalmirrorlists(fmr_handles, err); g_slist_free(fmr_handles); if (!ret) { return FALSE; } } // Start downloading ret = lr_download(downloadtargets, failfast, err); cleanup: // Copy download statuses from downloadtargets to targets for (GSList *elem = downloadtargets; elem; elem = g_slist_next(elem)) { LrDownloadTarget *downloadtarget = elem->data; LrPackageTarget *packagetarget = downloadtarget->userdata; if (downloadtarget->err) packagetarget->err = g_string_chunk_insert(packagetarget->chunk, downloadtarget->err); } // Free downloadtargets list g_slist_free_full(downloadtargets, (GDestroyNotify)lr_downloadtarget_free); // Restore original signal handler if (interruptible) { g_debug("%s: Restoring an old SIGINT handler", __func__); sigaction(SIGINT, &old_sigact, NULL); if (lr_interrupt) { if (err && *err != NULL) g_clear_error(err); g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_INTERRUPTED, "Insterupted by a SIGINT signal"); return FALSE; } } return ret; }