#!/bin/bash # # apt-fast v1.10.0 # Use this just like aptitude or apt-get for faster package downloading. # # Copyright: 2008-2012 Matt Parnell, http://www.mattparnell.com # Improvements, maintenance, revisions - 2012, 2017-2019 Dominique Lasserre # # You may distribute this file under the terms of the GNU General # Public License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # shopt -s nullglob [ -n "$DEBUG" ] && set -xv # Print colored messages. # Usage: msg "message text" "message type" "optional: err" # Message types are 'normal', 'hint' or 'warning'. Warnings and messages with a # third argument are piped to stderr. THREADS=$(nproc 2>/dev/null || echo 4) msg(){ msg_options=() case "$2" in normal) beginColor="$cGreen";; hint) beginColor="$cBlue";; warning) beginColor="$cRed";; question) beginColor="$cRed"; msg_options=(-n);; *) beginColor= ;; esac if [ -z "$3" ] && [ "$2" != "warning" ]; then echo -e "${msg_options[@]}" "${aptfast_prefix}${beginColor}$1${endColor}" else echo -e "${msg_options[@]}" "${aptfast_prefix}${beginColor}$1${endColor}" >&2 fi } # Search for known options and decide if root privileges are needed. root=$# option= for argument in "$@"; do case "$argument" in upgrade | full-upgrade | install | dist-upgrade | build-dep) option="install" ;; clean | autoclean) option="clean" ;; download) option="download" root=0 ;; source) option="source" root=0 ;; *) root=0 ;; esac done # To handle priority of options correctly (environment over config file vars) # we need to preserve all interesting env variables. As this wouldn't be # difficult enough we have to preserve complete env vars (especially if value # ist set (even empty) or not) when changing context (sudo)... # Set a 'random' string to all unset variables. TMP_RANDOM="13979853562951413" TMP_LCK_FILE="${LCK_FILE-${TMP_RANDOM}}" TMP_DOWNLOADBEFORE="${DOWNLOADBEFORE-${TMP_RANDOM}}" TMP__APTMGR="${_APTMGR-${TMP_RANDOM}}" TMP_APTCACHE="${APTCACHE-${TMP_RANDOM}}" TMP_DLDIR="${DLDIR-${TMP_RANDOM}}" TMP_DLLIST="${DLLIST-${TMP_RANDOM}}" TMP__MAXNUM="${MAXNUM-${TMP_RANDOM}}" TMP__MAXCONPERSRV="${MAXCONPERSRV-${TMP_RANDOM}}" TMP__SPLITCON="${SPLITCON-${TMP_RANDOM}}" TMP__MINSPLITSZ=${MINSPLITSZ-${TMP_RANDOM}} TMP__PIECEALGO=${PIECEALGO-${TMP_RANDOM}} TMP_aptfast_prefix="${aptfast_prefix-${TMP_RANDOM}}" TMP_APT_FAST_TIMEOUT="${APT_FAST_TIMEOUT-${TMP_RANDOM}}" TMP_APT_FAST_APT_AUTH="${APT_FAST_APT_AUTH-${TMP_RANDOM}}" TMP_VERBOSE_OUTPUT="${VERBOSE_OUTPUT-${TMP_RANDOM}}" TMP_ftp_proxy="${ftp_proxy-${TMP_RANDOM}}" TMP_http_proxy="${http_proxy-${TMP_RANDOM}}" TMP_https_proxy="${https_proxy-${TMP_RANDOM}}" # Check for proper privileges. # Call explicitly with environment variables to get them into root conext. if [ "$root" -ne 0 ] && [ "$UID" != 0 ]; then exec sudo DEBUG="$DEBUG" \ LCK_FILE="$TMP_LCK_FILE" \ DOWNLOADBEFORE="$TMP_DOWNLOADBEFORE" \ _APTMGR="$TMP__APTMGR" \ APTCACHE="$TMP_APTCACHE" \ DLDIR="$TMP_DLDIR" \ DLLIST="$TMP_DLLIST" \ _MAXNUM="$TMP__MAXNUM" \ _MAXCONPERSRV="$TMP__MAXCONPERSRV" \ _SPLITCON="$TMP__SPLITCON" \ _MINSPLITSZ="$TMP__MINSPLITSZ" \ _PIECEALGO="$TMP__PIECEALGO" \ aptfast_prefix="$TMP_aptfast_prefix" \ APT_FAST_TIMEOUT="$TMP_APT_FAST_TIMEOUT" \ APT_FAST_APT_AUTH="$TMP_APT_FAST_APT_AUTH" \ VERBOSE_OUTPUT="$TMP_VERBOSE_OUTPUT" \ ftp_proxy="$TMP_ftp_proxy" \ http_proxy="$TMP_http_proxy" \ https_proxy="$TMP_https_proxy" \ "$0" "$@" fi # Define lockfile. # Use /tmp as directory because everybody (not only root) has to have write # permissions. # We need lock for non-root commands too, because we only have one download # list file. if [ "$IS_ACE_ENV" != "" ];then LCK_FILE="/tmp/apt-fast-in-container.lock" else LCK_FILE="/tmp/apt-fast.lock" fi LCK_FD=99 # Set default package manager, APT cache, temporary download dir, # temporary download list file, and maximal parallel downloads _APTMGR=apt-get eval "$(apt-config shell APTCACHE Dir::Cache::archives/d)" # Check if APT config option Dir::Cache::archives::apt-fast-partial is set. eval "$(apt-config shell apt_fast_partial Dir::Cache::archives::apt-fast-partial/d)" if [ -z "$apt_fast_partial" ]; then DLDIR="$(realpath "${APTCACHE}/../apt-fast")" else DLDIR="${apt_fast_partial}" fi # Check for apt auth files eval "$(apt-config shell NETRC Dir::Etc::netrc/f)" eval "$(apt-config shell NETRCDIR Dir::Etc::netrcparts/d)" APTAUTHFILES=() if [ -f "$NETRC" ]; then APTAUTHFILES=("$NETRC") fi APTAUTHFILES+=("$NETRCDIR"*) if [ "$IS_ACE_ENV" != "" ];then DLLIST="/tmp/apt-fast-in-container.list" else DLLIST="/tmp/apt-fast.list" fi _MAXNUM=5 _MAXCONPERSRV=10 _SPLITCON=8 _MINSPLITSZ="1M" _PIECEALGO="default" MIRRORS=() # Prefix in front of apt-fast output: aptfast_prefix= # aptfast_prefix="$(date '+%b %_d %T.%N') apt-fast: " # Set color variables. cGreen='\e[0;32m' cRed='\e[0;31m' cBlue='\e[0;34m' endColor='\e[0m' # Set timout value for apt-fast download confirmation dialog. # Value is in seconds. APT_FAST_TIMEOUT=60 # Ask for download confirmation if unset DOWNLOADBEFORE= # Enable APT authentication support APT_FAST_APT_AUTH=1 # Formatted package list in download confirmation if unset VERBOSE_OUTPUT= # Download command. _DOWNLOADER='aria2c --no-conf -c -j ${_MAXNUM} -x ${_MAXCONPERSRV} -s ${_SPLITCON} -i ${DLLIST} --min-split-size=${_MINSPLITSZ} --stream-piece-selector=${_PIECEALGO} --connect-timeout=60 --timeout=600 -m0' # 定义默认的配置文件列表(按加载顺序排列) CONFIG_FILES=( "/tmp/aptss-conf/apt-fast.conf" # 原始配置文件位置 "/etc/aptss/apt-fast.conf" # 系统级配置 ) # 按顺序加载所有配置文件 for conf_file in "${CONFIG_FILES[@]}"; do if [ -e "$conf_file" ]; then source "$conf_file" fi done # no proxy as default ftp_proxy= http_proxy= https_proxy= # Now overwrite with preserved values if values were set before (compare with # 'random' string). [ "$TMP_LCK_FILE" = "$TMP_RANDOM" ] || LCK_FILE="$TMP_LCK_FILE" [ "$TMP_DOWNLOADBEFORE" = "$TMP_RANDOM" ] || DOWNLOADBEFORE="$TMP_DOWNLOADBEFORE" [ "$TMP__APTMGR" = "$TMP_RANDOM" ] || _APTMGR="$TMP__APTMGR" [ "$TMP_APTCACHE" = "$TMP_RANDOM" ] || APTCACHE="$TMP_APTCACHE" [ "$TMP_DLDIR" = "$TMP_RANDOM" ] || DLDIR="$TMP_DLDIR" [ "$TMP_DLLIST" = "$TMP_RANDOM" ] || DLLIST="$TMP_DLLIST" [ "$TMP__MAXNUM" = "$TMP_RANDOM" ] || _MAXNUM="$TMP__MAXNUM" [ "$TMP__MAXCONPERSRV" = "$TMP_RANDOM" ] || _MAXCONPERSRV="$TMP__MAXCONPERSRV" [ "$TMP__SPLITCON" = "$TMP_RANDOM" ] || _SPLITCON="$TMP__SPLITCON" [ "$TMP__MINSPLITSZ" = "$TMP_RANDOM" ] || _MINSPLITSZ="$TMP__MINSPLITSZ" [ "$TMP__PIECEALGO" = "$TMP_RANDOM" ] || _PIECEALGO="$TMP__PIECEALGO" [ "$TMP_aptfast_prefix" = "$TMP_RANDOM" ] || aptfast_prefix="$TMP_aptfast_prefix" [ "$TMP_APT_FAST_TIMEOUT" = "$TMP_RANDOM" ] || APT_FAST_TIMEOUT="$TMP_APT_FAST_TIMEOUT" [ "$TMP_APT_FAST_APT_AUTH" = "$TMP_RANDOM" ] || APT_FAST_APT_AUTH="$TMP_APT_FAST_APT_AUTH" [ "$TMP_VERBOSE_OUTPUT" = "$TMP_RANDOM" ] || VERBOSE_OUTPUT="$TMP_VERBOSE_OUTPUT" [ "$TMP_ftp_proxy" = "$TMP_RANDOM" ] || ftp_proxy="$TMP_ftp_proxy" [ "$TMP_http_proxy" = "$TMP_RANDOM" ] || http_proxy="$TMP_http_proxy" [ "$TMP_https_proxy" = "$TMP_RANDOM" ] || https_proxy="$TMP_https_proxy" # Disable colors if not executed in terminal. if [ ! -t 1 ]; then cGreen= cRed= cBlue= endColor= #FIXME: Time not updated. [ -z "$aptfast_prefix" ] && aptfast_prefix="[apt-fast $(date +"%T")]" fi msg_already_running() { msg "Other aptss is running. Waited $timer senconds..." "normal" msg "有其他的aptss正在运行。已经等待了$timer秒" "normal" } # Check if a lock file exists. #if [ -f "$LCK_FILE.lock" ]; then # msg_already_running # exit 1 #fi # create the lock file and lock it, die on failure _create_lock() { eval "exec $LCK_FD>\"$LCK_FILE.lock\"" # 设置 trap 来清理资源 trap "cleanup_aptfast" EXIT trap "cleanup_aptfast; exit 1" INT TERM timer=0 max_wait=180 # 最大等待时间为180秒(3分钟) until $(flock -xn $LCK_FD); do msg_already_running sleep 1 let timer+=1 if [ $timer -ge $max_wait ]; then echo "timeout" exit 1 fi done unset timer } # unlock and remove the lock file _remove_lock() { flock -u "$LCK_FD" 2>/dev/null rm -f "$LCK_FILE.lock" } # Move download file away so missing permissions won't stop usage. CLEANUP_STATE=0 cleanup_dllist() { if [ -f "$DLLIST" ] then if ! mv -- "$DLLIST{,.old}" 2>/dev/null then if ! rm -f -- "$DLLIST" 2>/dev/null then msg "Could not clean up download list file." "warning" msg "无法清除下载列表文件." "warning" CLEANUP_STATE=1 fi fi fi } cleanup_aptfast() { local last_exit_code=$? [ "$CLEANUP_STATE" -eq 0 ] && CLEANUP_STATE=$last_exit_code cleanup_dllist _remove_lock } exit_cleanup_state() { exit $CLEANUP_STATE } # decode url string # translates %xx but must not convert '+' in spaces urldecode() { printf '%b' "${1//%/\\x}" } # Check if mirrors are available. And if so add all mirrors to download list. ############ SPARK ADJUST: Now we ignore the first config for business request get_mirrors(){ # Check all mirror lists. for mirrorstr in "${MIRRORS[@]}"; do # Build mirrors array from comma separated string. IFS=", " read -r -a mirrors <<< "$mirrorstr" # Check for all mirrors if URI of $1 is from mirror. If so add all other # mirrors to (resmirror) list and break all loops. for mirror in "${mirrors[@]}"; do # Real expension. if [[ "$1" == "$mirror"* ]]; then filepath="${1#"${mirror}"}" # Build list for aria download list. list="${mirrors[*]:1}" echo -e "${list// /${filepath}\\t}$filepath\n" return 0 fi done done # No other mirrors found. echo "$1" } ##########SPARK ADJUST: END AUTH_INFO_PARSED=() # Parse apt authentication files. # Undefined behavior on whitespaces in host, username or password. prepare_auth(){ if [ "$APT_FAST_APT_AUTH" -eq 0 ]; then return fi for auth_file in "${APTAUTHFILES[@]}"; do # auth files have netrc syntax, possible multiline entries starting with "machine" auth_info="$(tr '\n' ' ' < "$auth_file" | sed 's/\(\<machine\>\)/\n\1/g' | sed '1d')" while IFS= read -r auth; do machine="$(echo "$auth" | sed 's/.*\<machine\>[ \t]\+\([^ \t]\+\).*/\1/')" login="$(echo "$auth" | sed 's/.*\<login\>[ \t]\+\([^ \t]\+\).*/\1/')" password="$(echo "$auth" | sed 's/.*\<password\>[ \t]\+\([^ \t]\+\).*/\1/')" # if machine does not have protocol, try https:// if ! [[ "$machine" =~ ^.*:// ]]; then machine="https://$machine" fi if [ -z "$machine" ] || [ -z "$login" ] || [ -z "$password" ]; then msg "Could not parse apt authentication (skipping): $auth ($auth_file)" "warning" continue fi # use space separated string to convert back to array later AUTH_INFO_PARSED+=("$machine $login $password") done <<< "$auth_info" done if [ "${#AUTH_INFO_PARSED[@]}" -eq 0 ]; then # acts like auth disabled when no auth info is provided to improve performance APT_FAST_APT_AUTH=0 fi } # Gets URI as parameter and tries to add basic http credentials. Will fail on # credentials that contain characters that need URL-encoding. get_auth(){ for auth_info in "${AUTH_INFO_PARSED[@]}"; do # convert to array, don't escape variable here auth_info_arr=($auth_info) machine="${auth_info_arr[0]}" # takes first match if [[ "$1" == "$machine"* ]]; then login="${auth_info_arr[1]}" password="${auth_info_arr[2]}" uri="$(echo "$1" | sed "s|^\([^:]\+://\)|\1$login:$password@|")" echo "$uri" return fi done echo "$1" } # Globals to save package name, version, size and overall size. DOWNLOAD_DISPLAY= DOWNLOAD_SIZE=0 # 获取包的URI # Get the package URLs. get_uris(){ if [ ! -d "$(dirname "$DLLIST")" ] then if ! mkdir -p -- "$(dirname "$DLLIST")" then msg "Could not create download file directory." "warning" msg "无法创建下载目录" "warning" CLEANUP_STATE=1 exit fi elif [ -f "$DLLIST" ]; then if ! rm -f -- "$DLLIST" 2>/dev/null && ! touch -- "$DLLIST" 2>/dev/null then msg "Unable to write to download file. Try restarting with root permissions or run 'apt-fast clean' first." "warning" msg "无法下载文件。尝试使用root权限,或者运行 'aptss clean'" "warning" CLEANUP_STATE=1 exit fi fi # Add header to overwrite file. echo "# apt-fast mirror list: $(date)" > "$DLLIST" # NOTE: "aptitude" doesn't have this functionality # so we use "${_APTMGR}" to get package URI's case "$(basename "${_APTMGR}")" in 'apt'|'apt-get') uri_mgr="${_APTMGR}";; *) uri_mgr='apt-get';; esac uris_full="$("$uri_mgr" "${APT_SCRIPT_WARNING[@]}" -y --print-uris "$@")" CLEANUP_STATE="$?" if [ "$CLEANUP_STATE" -ne 0 ] then msg "Package manager quit with exit code. Here is the log" "warning" msg "包管理器以错误代码退出.日志如下" "warning" msg "${uris_full}" exit "$CLEANUP_STATE" fi prepare_auth local tmpdir tmpdir=$(mktemp -d) || { msg "Failed to create tmp dir" "warning" msg "无法创建临时目录" "warning" exit 1 } ## --print-uris format is: # 'fileurl' filename filesize checksum_hint:filechecksum # 修改:process_package函数增加第二个参数表示当前线程的临时输出文件 process_package() { local pkg_uri_info="$1" local thread_file="$2" local display_line="" # 初始化显示信息为空 IFS=' ' read -r uri filename filesize checksum_string _ <<<"$pkg_uri_info" [ -z "$uri" ] && return uri="${uri//"'"/}" [ "$APT_FAST_APT_AUTH" -ne 0 ] && uri="$(get_auth "$uri")" IFS=':' read -r hash_algo checksum _ <<<"$checksum_string" if [[ "$filename" == *%* ]]; then # decode url string filename_decoded="$(printf '%b' "${filename//%/\\x}")" else filename_decoded="$filename" fi IFS='_' read -r pkg_name_decoded pkg_version_decoded _ <<<"$filename_decoded" display_line+="$pkg_name_decoded $pkg_version_decoded $filesize\n" if [ -n "$HASH_SUPPORTED" ]; then case "$hash_algo" in SHA512) [ -z "$SHA512_SUPPORTED" ] && hash_algo= || hash_algo=sha-512 ;; SHA256) [ -z "$SHA256_SUPPORTED" ] && hash_algo= || hash_algo=sha-256 ;; SHA1) [ -z "$SHA1_SUPPORTED" ] && hash_algo= || hash_algo=sha-1 ;; MD5Sum) [ -z "$MD5sum_SUPPORTED" ] && hash_algo= || hash_algo=md5 ;; *) hash_algo= esac # Using apt-cache show package=version to ensure recover single and # correct package version. # Warning: assuming that package naming uses '_' as field separator. # Therefore, this code expects package-name_version_arch.deb Otherwise # below code will fail resoundingly if [ -z "$hash_algo" ]; then IFS='_' read -r pkg_name _ <<<"$filename" pkg_version="$pkg_version_decoded" # Transform multi-line field output from apt-cache to single line and sort checksums, strongest first package_info="$(apt-cache show "$pkg_name=$pkg_version" | sed ':r;$!{N;br};s/\n / /g' | sort -r)" while IFS=': ' read -r field checksum _ do case "$field" in SHA512) [ -n "$SHA512_SUPPORTED" ] || continue hash_algo="sha-512" break ;; SHA256) [ -n "$SHA256_SUPPORTED" ] || continue hash_algo="sha-256" break ;; SHA1) [ -n "$SHA1_SUPPORTED" ] || continue hash_algo="sha-1" break ;; MD5sum) [ -n "$MD5sum_SUPPORTED" ] || continue hash_algo="md5" break ;; esac done <<<"$package_info" if [ -z "$hash_algo" ]; then checksum= msg "Couldn't get supported checksum for $pkg_name ($pkg_version)." "warning" msg "无法获得 $pkg_name ($pkg_version) 版本受到支持的散列验证值" "warning" REMOVE_WORKING_MESSAGE= fi fi else hash_algo= fi # 原来利用文件锁写入,现在改为写入当前线程的临时文件 { get_mirrors "$uri" [ -n "$hash_algo" ] && echo " checksum=$hash_algo=$checksum" echo " out=$filename" } >> "$thread_file" echo -e "$display_line" >> "$tmpdir/display" echo "$filesize" >> "$tmpdir/sizes" } # 主并行处理逻辑 mapfile -t pkg_uri_list < <(echo "$uris_full" | grep -E "^'(http(s|)|(s|)ftp)://") total_pkgs=${#pkg_uri_list[@]} threads=${THREADS:-4} # 默认4线程 per_thread=$(( (total_pkgs + threads - 1) / threads )) # 向上取整 # 分配任务到不同线程,每个线程使用自己的临时文件 for ((i=0; i<threads; i++)); do thread_file="${DLLIST}.thread.${i}" > "$thread_file" # 清空或创建临时文件 start=$((i * per_thread)) end=$((start + per_thread -1)) [ $end -ge $total_pkgs ] && end=$((total_pkgs -1)) # 启动后台线程处理任务块 ( for ((j=start; j<=end; j++)); do [ -z "${pkg_uri_list[j]}" ] && continue process_package "${pkg_uri_list[j]}" "$thread_file" done ) & done # 等待所有后台任务完成 wait # 合并所有线程的临时文件到最终的 $DLLIST 中(保留之前添加的 header) for ((i=0; i<threads; i++)); do thread_file="${DLLIST}.thread.${i}" if [ -f "$thread_file" ]; then cat "$thread_file" >> "$DLLIST" rm -f "$thread_file" fi done # 合并显示信息 if [ -f "$tmpdir/display" ]; then DOWNLOAD_DISPLAY+="\n$(cat "$tmpdir/display")" fi # 计算总下载大小 if [ -f "$tmpdir/sizes" ]; then DOWNLOAD_SIZE=$(awk '{sum+=$1} END{print sum}' "$tmpdir/sizes") fi # 清理临时目录 rm -rf "$tmpdir" } display_downloadfile(){ if [ -n "$VERBOSE_OUTPUT" ]; then cat "$DLLIST" else DISPLAY_SORT_OPTIONS=(-k 1,1) # Sort output after package download size (decreasing): #DISPLAY_SORT_OPTIONS=(-k 3,3 -hr) while IFS=' ' read -r pkg ver size _; do [ -z "$pkg" ] && continue printf '%s%-40s %-20s %10s\n' "$aptfast_prefix" "$pkg" "$ver" "$size" done <<<"$(echo -e "$DOWNLOAD_DISPLAY" | sort "${DISPLAY_SORT_OPTIONS[@]}" | numfmt --to=iec-i --suffix=B --field=3)" fi msg "Download size: $(echo "$DOWNLOAD_SIZE" | numfmt --to=iec-i --suffix=B)" "normal" msg "下载大小: $(echo "$DOWNLOAD_SIZE" | numfmt --to=iec-i --suffix=B)" "normal" } # Create and insert a PID number to lockfile. _create_lock # Make sure aria2c (in general first parameter from _DOWNLOADER) is available. CMD="$(echo "$_DOWNLOADER" | sed 's/^\s*\([^ ]\+\).*$/\1/')" if [ ! "$(command -v "$CMD")" ]; then msg "Command not found: $CMD" "normal" "err" msg "You must configure $CONFFILE to use aria2c or another supported download manager" "normal" "err" CLEANUP_STATE=1 exit fi # Make sure package manager is available. if [ ! "$(command -v "$_APTMGR")" ]; then msg "\`$_APTMGR\` command not available." "warning" msg "You must configure $CONFFILE to use either apt-get or aptitude." "normal" "err" CLEANUP_STATE=1 exit fi # Disable script warning if apt is used. APT_SCRIPT_WARNING=() if [ "$(basename "${_APTMGR}")" == 'apt' ]; then APT_SCRIPT_WARNING=(-o "Apt::Cmd::Disable-Script-Warning=true") fi # Set supported hash algorithms by aria2c (and also by Debian repository). SHA512_SUPPORTED= SHA256_SUPPORTED= SHA1_SUPPORTED= MD5sum_SUPPORTED= HASH_SUPPORTED= if [ "$CMD" == "aria2c" ]; then for supported_hash in $(LC_ALL=C aria2c -v | sed '/^Hash Algorithms:/!d; s/\(^Hash Algorithms: \|,\)\+//g'); do case "$supported_hash" in sha-512) SHA512_SUPPORTED=y; HASH_SUPPORTED=y ;; sha-256) SHA256_SUPPORTED=y; HASH_SUPPORTED=y ;; sha-1) SHA1_SUPPORTED=y; HASH_SUPPORTED=y ;; md5) MD5sum_SUPPORTED=y; HASH_SUPPORTED=y ;; esac done if [ -z "$HASH_SUPPORTED" ]; then msg "Couldn't find supported checksum algorithm from aria2c. Checksums disabled." "warning" msg "无法找到aria2c支持的散列验证算法. 散列验证已被禁用." "warning" fi fi # Check if "assume yes" switch is enabled and if yes enable $DOWNLOADBEFORE. # Also check if "download only" switch is enabled. #TODO: Get real value over APT items APT::Get::Assume-Yes and # APT::Get::Assume-No . # Respectively Aptitude::CmdLine::Download-Only and APT::Get::Download-Only. DOWNLOAD_ONLY= while true; do while getopts ":dy-:" optchar; do case "${optchar}" in -) case "${OPTARG}" in yes | assume-yes) DOWNLOADBEFORE=true ;; assume-no) DOWNLOADBEFORE= ;; download-only) DOWNLOAD_ONLY=true ;; esac ;; y) DOWNLOADBEFORE=true ;; d) DOWNLOAD_ONLY=true ;; *) ;; esac done ((OPTIND++)) [ $OPTIND -gt $# ] && break done # Configure proxies. Use apt values over environment variables. # Note: If proxy setting is not set, there is no apt-config output. # Therefore variable doesn't get overriden, which is intended. # Export the variables to make them available in subshells (aka the # downloader command). eval "$(apt-config shell ftp_proxy Acquire::ftp::proxy)" export ftp_proxy eval "$(apt-config shell http_proxy Acquire::http::proxy)" export http_proxy eval "$(apt-config shell https_proxy Acquire::https::proxy)" export https_proxy # aria2 has no socks support (see https://github.com/aria2/aria2/issues/153) if echo "$http_proxy" | grep -q "^socks5h://" || echo "$https_proxy" | grep -q "^socks5h://"; then msg "Socks proxy detected. Falling back to ${_APTMGR}" "hint" "${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@" exit 0 fi # Run actions. if [ "$option" == "install" ]; then msg msg "Working... this may take a while." "normal" msg "正在工作中,请稍等" "normal" REMOVE_WORKING_MESSAGE=y get_uris "$@" [ -t 1 ] && [ -n "$REMOVE_WORKING_MESSAGE" ] && tput cuu 1 && tput el && tput cuu 1 # Test /tmp/apt-fast.list file exists and not just the apt-fast comment line. # Then download all files from the list. if [ -f "$DLLIST" ] && [ "$(wc -l "$DLLIST" | cut -d' ' -f1)" -gt 1 ] && [ ! "$DOWNLOADBEFORE" ]; then display_downloadfile msg msg "Do you want to download the packages? [Y/n] " "question" while ((!updsys)); do read -r -sn1 -t "$APT_FAST_TIMEOUT" answer || { msg; msg "Timed out." "warning"; CLEANUP_STATE=1; exit; } case "$answer" in [JjYy]) result=1; updsys=1 ;; [Nn]) result=0; updsys=1 ;; "") result=1; updsys=1 ;; *) updsys=0 ;; esac done else result=1 fi if ((DOWNLOAD_SIZE)); then msg # Continue if answer was right or DOWNLOADBEFORE is enabled. if ((result)); then if [ -s "$DLLIST" ]; then # Test if apt-fast directory is present where we put packages. if [ ! -d "$DLDIR" ]; then mkdir -p -- "$DLDIR" fi cd "$DLDIR" &>/dev/null || { msg; msg "Not able to change into download directory." "warning"; CLEANUP_STATE=1; exit; } eval "${_DOWNLOADER}" # execute downloadhelper command if [ "$(find "$DLDIR" -printf . | wc -c)" -gt 1 ]; then # Delete incomplete/corrupted downloaded files, if any: Not recursive, as we don't expect any dirs to exist within $DLDIR. # When Aria2c downloads a file and detects it is corrupted, its filename won't be renamed back to its actual name, # preserving .aria2 file extension, which also indicates when a file hasn't been completely downloaded. for x in *.aria2; do rm -f "$x" "${x%.aria2}" done # Move all packages to the apt install directory by force to ensure # already existing debs which may be incomplete are replaced find . -type f \( -name '*.deb' -o -name '*.ddeb' \) -execdir mv -ft "$APTCACHE" {} \+ fi cd - &>/dev/null || msg "Failed to change back directory" "warning" fi else CLEANUP_STATE=1 exit fi else [ -t 1 ] && tput el fi # different problem resolving for aptitude if [ -z "$DOWNLOAD_ONLY" ] || [ "$(basename "${_APTMGR}")" == 'aptitude' ]; then "${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@" fi elif [ "$option" == "clean" ]; then "${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@" && { if [ -d "$DLDIR" ]; then find "$DLDIR" -maxdepth 1 -type f -delete CLEANUP_STATE="$?" [ -f "$DLLIST" ] && rm -f -- "$DLLIST"* || true fi } elif [ "$option" == "download" ]; then msg msg "Working... this may take a while." "normal" msg "正在工作中,请稍等" "normal" REMOVE_WORKING_MESSAGE=y get_uris "$@" [ -t 1 ] && [ -n "$REMOVE_WORKING_MESSAGE" ] && tput cuu 1 && tput el && tput cuu 1 if [ -f "$DLLIST" ] && [ "$(wc -l "$DLLIST" | cut -d' ' -f1)" -gt 1 ]; then display_downloadfile eval "${_DOWNLOADER}" fi # different problem resolving for aptitude if [ "$(basename "${_APTMGR}")" == 'aptitude' ]; then "${_APTMGR}" "$@" fi elif [ "$option" == "source" ]; then msg msg "Working... this may take a while." "normal" msg "正在工作中,请稍等" "normal" REMOVE_WORKING_MESSAGE=y get_uris "$@" [ -t 1 ] && [ -n "$REMOVE_WORKING_MESSAGE" ] && tput cuu 1 && tput el && tput cuu 1 if [ -f "$DLLIST" ] && [ "$(wc -l "$DLLIST" | cut -d' ' -f1)" -gt 1 ]; then display_downloadfile eval "${_DOWNLOADER}" fi # We use APT manager here to provide more verbose output. This method is # slightly slower then extractiong packages manually after download but also # more hardened (e.g. some options like --compile are available). "${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@" # Uncomment following snippet to extract source directly and comment # both lines before. #while read srcfile; do # # extract only .dsc files # echo "$srcfile" | grep -q '\.dsc$' || continue # dpkg-source -x "$(basename "$srcfile")" #done < "$DLLIST" # Execute package manager directly if unknown options are passed. else "${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@" fi # After error or all done remove our lockfile (done with EXIT trap)