commit: fd3ffe0c512b8d711c4512a53b244fda9951bd19
Author: Kerin Millar <kfm <AT> plushkava <DOT> net>
AuthorDate: Wed Jul 2 09:33:58 2025 +0000
Commit: Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Fri Jul 4 02:17:03 2025 +0000
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=fd3ffe0c
emerge-webrsync: don't append options to FETCHCOMMAND in fetch_file()
Presently, the fetch_file() function relies upon the portage variable
named 'FETCHCOMMAND' in order to be able to execute a user-agent. By and
large, portage uses Python's shlex library to split the value of the
variable. By contrast, emerge-webrsync uses the eval builtin to treat
the value of the variable as shell code. Before doing so, the function
employs a heuristic to determine whether curl(1) or wget(1) will be
executed, potentially appending some options that are unique to those
programs. In doing so, it may inadvertently cause wget(1) to fail.
Imagine that the 'FETCHCOMMAND' variable has been defined in the
following way.
# make.conf syntax
FETCHCOMMAND="wget -O \"\${DISTDIR}/\${FILE}\" \"\${URI}\""
Consequently, the literal value of the variable is as follows.
wget -O "${DISTDIR}/${FILE}" "${URI}"
The fetch_file() function will match "wget" as a substring and append
some options.
wget -O "${DISTDIR}/${FILE}" "${URI}" --continue --no-verbose
In most cases, wget(1) will proceed to behave as expected, but only
because gnulib allows for options to be permuted by default. That is,
option processing is normally allowed to continue after encountering a
non-option argument. This default behaviour can be suppressed by
defining the 'POSIXLY_CORRECT' environment variable, in which case the
options will be treated as URLs instead.
+ eval 'wget "${DISTDIR}/${FILE}" "${URI}" --continue --no-verbose'
--2025-07-02 09:40:09-- http://distfiles.gentoo.org/snapshots/gentoo-20250701.>
Prepended http:// to '--continue'
--2025-07-02 09:40:12-- http://--continue/
Resolving --continue... failed: Name or service not known.
wget: unable to resolve host address ‘--continue’
Address this issue by using the read builtin to split the value of
'FETCHCOMMAND' into two variables, one of which contains the ostensible
command name, and the other of which contains the remainder. If the name
is found to match exactly "curl" or "wget", the options will now be
injected in such a way that they immediately follow it.
Link:
https://cgit.git.savannah.gnu.org/cgit/gnulib.git/tree/lib/getopt-core.h?h=v1.0#n81
Signed-off-by: Kerin Millar <kfm <AT> plushkava.net>
Signed-off-by: Sam James <sam <AT> gentoo.org>
bin/emerge-webrsync | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/bin/emerge-webrsync b/bin/emerge-webrsync
index bf86ae763e..936f832637 100755
--- a/bin/emerge-webrsync
+++ b/bin/emerge-webrsync
@@ -196,29 +196,33 @@ is_uint() {
fetch_file() {
# shellcheck disable=2034
local URI=$1 FILE=$2
- local opts
+ local cmd_name cmd_args opts
- case ${FETCHCOMMAND} in
- *wget*)
+ read -rd '' cmd_name cmd_args <<<"${FETCHCOMMAND}"
+
+ case ${cmd_name} in
+ wget)
opts="--continue --no-verbose"
if (( ! opt[quiet] )); then
opts+=" --show-progress"
fi
;;
- *curl*)
+ curl)
opts="--continue-at -f -S"
if (( opt[quiet] )); then
opts+=" -s"
fi
;;
*)
+ cmd_name=${FETCHCOMMAND}
+ cmd_args=
rm -f -- "${DISTDIR}/${FILE}"
esac
einfo "Fetching file ${FILE} ..."
# Already set DISTDIR=
- if ! eval "${FETCHCOMMAND} ${opts}" || [[ ! -s ${DISTDIR}/${FILE} ]];
then
+ if ! eval "${cmd_name} ${opts} ${cmd_args}" || [[ ! -s
${DISTDIR}/${FILE} ]]; then
rm -f -- "${DISTDIR}/${FILE}"
return 1
fi