2025-04-13 19:13:08 +08:00

840 lines
26 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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)