2007-03-05 01:15:05 +03:00
#!/bin/sh -efu
2002-03-25 23:37:46 +03:00
#
2007-08-08 00:49:48 +04:00
# This file provides FindPackage() function which maps paths and
# commands, such as found in shell scripts, to rpm dependencies.
#
# Usage:
# . /usr/lib/rpm/find-package
# FindPackage src [path...] [command...]
#
# Arguments:
2007-09-18 23:46:02 +04:00
# src - the file being processed, used for diagnostics;
# also, if the file appears to reside under */sbin/
# directory, the PATH search order is adjusted
# so as to better match root code
2007-08-08 00:49:48 +04:00
# path - absolute path to file, e.g. /bin/cat
# command - executable expected to reside under standard
# PATH directories, e.g. cat
#
2003-04-22 19:11:52 +04:00
# Copyright (C) 2002-2003 Dmitry V. Levin <ldv@altlinux.org>
2007-03-08 16:49:21 +03:00
# Copyright (C) 2007 Alexey Tourbin <at@altlinux.org>
2002-03-25 23:37:46 +03:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
2003-11-09 19:47:45 +03:00
. @RPMCONFIGDIR@/functions
2002-03-25 23:37:46 +03:00
2007-09-18 23:46:02 +04:00
# We use different PATHs for */sbin/ and non-/sbin scripts. However,
# the distinction is not quite reliable -- there is simply no easy way
# to discriminate between root-only and user-capable shell code. Thus
# we must use rather conservative path adjustment: 1) the set of user
# and root directories is the same, it is only the order that differs;
# 2) / has main priority over /usr, while as "bin vs sbin" distinction has
# only secondary priority. The reason is that / has only "most important"
# contents, and /usr is used virtually "for everything else", whatever it is.
# Now that / has a boost, there are simply less chances to end up with
# unrelated dependencies.
DEF_RPM_FINDPACKAGE_USER_PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/games
DEF_RPM_FINDPACKAGE_ROOT_PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/usr/games
# RPM_FINDPACKAGE_PATH is exported by rpm-build
RPM_FINDPACKAGE_USER_PATH="$(IFS="$IFS:"; set -f; echo '' ${RPM_FINDPACKAGE_PATH-} $DEF_RPM_FINDPACKAGE_USER_PATH |sed -e 's/ */:/g; s/^://')"
RPM_FINDPACKAGE_ROOT_PATH="$(IFS="$IFS:"; set -f; echo '' ${RPM_FINDPACKAGE_PATH-} $DEF_RPM_FINDPACKAGE_ROOT_PATH |sed -e 's/ */:/g; s/^://')"
Debug "RPM_FINDPACKAGE_USER_PATH=$RPM_FINDPACKAGE_USER_PATH"
Debug "RPM_FINDPACKAGE_ROOT_PATH=$RPM_FINDPACKAGE_ROOT_PATH"
2007-03-08 21:54:08 +03:00
2007-08-07 21:04:23 +04:00
# Below we use 'local Verbose=Info' to increase per-case verbosity.
Verbose=Verbose
2007-03-08 15:20:02 +03:00
FindByPath()
2002-03-25 23:37:46 +03:00
{
2007-03-08 15:20:02 +03:00
# Dependence name starts with `/'.
local f="$1" rep="$2" package; shift 2 || return
2002-03-25 23:37:46 +03:00
2007-03-08 15:20:02 +03:00
# Does it start with buildroot?
if [ -n "${RPM_BUILD_ROOT-}" ] && [ -z "${rep##$RPM_BUILD_ROOT*}" ]; then
Info "$f: invalid dependence: $rep"
return 1
fi
2003-04-22 19:11:52 +04:00
2007-03-08 15:20:02 +03:00
# Does it belong to buildroot?
if [ -n "${RPM_BUILD_ROOT-}" ] && [ -e "$RPM_BUILD_ROOT$rep" ]; then
2007-08-07 21:04:23 +04:00
$Verbose "$f: $rep -> \$RPM_BUILD_ROOT$rep (skip)"
2007-03-08 15:20:02 +03:00
return
fi
2003-04-22 19:11:52 +04:00
2007-03-08 15:20:02 +03:00
# Is it an alternative?
if readlink "$rep" |grep -qs '^/etc/alternatives/'; then
2007-08-07 21:04:23 +04:00
$Verbose "$f: $rep -> $rep (alternative)"
2007-03-08 15:20:02 +03:00
printf %s\\n "$rep"
return
fi
2003-04-22 19:11:52 +04:00
2007-08-27 21:21:55 +04:00
# XXX Cleanup pathname.
rep=$(CanonPath "$rep")
find-package (FindByPath): enhanced contents_index search
In FindByPath, we should always check contents_index_bin first.
It is not quite expensive, and we make no assumptions which path
entries it may contain (our contents_index_bin, in addition
to standard *bin/ paths, also has /etc entries).
However, if contents_index_bin lookup fails, we may or may
not want to proceed with very expensive contents_index_all
search. We assume that, if contents_index_bin lookup actually
has take place for some standard *bin/ path, then there is simply
no need to proceed with contents_index_all.
Now we also assume that contents_index_all file can be possibly
gzipped (I use "gzip -cdfq" for "zcat or cat", found in zgrep).
Also increased verbosity (Verbose to Info) for contents_index_all
messages, since it is expensive and it is a means of "last resort"
to finding something strange before giving up, which is quite worth
to note about.
Also increased verbosity for "raw, not found" dependencies,
since they are likely to become unmet.
2007-08-07 17:32:46 +04:00
# Always try package binary index.
local idx_bin="${RPM_PKG_CONTENTS_INDEX_BIN-}" try_idx_bin=1
[ -n "$idx_bin" ] && [ -s "$idx_bin" ] && [ -r "$idx_bin" ] || try_idx_bin=
if [ -n "$try_idx_bin" ]; then
package="$(awk -v "f=$rep" '($1 == f) {print $2}' "$idx_bin" |sort -u)"
2007-03-08 16:49:21 +03:00
local n="$(IFS=$'\n'; set -- $package; echo $#)"
if [ "$n" = 1 ]; then
2007-08-07 21:04:23 +04:00
$Verbose "$f: $rep -> $package (via contents_index_bin)"
2007-03-08 15:20:02 +03:00
printf %s\\n "$package"
return
2007-03-08 16:49:21 +03:00
elif [ "$n" -gt 1 ]; then
Info "$f: $rep indexed by:$(echo '' $package)"
2007-08-07 21:04:23 +04:00
Info "$f: $rep -> $rep (raw, ambiguous, via contents_index_bin)"
2007-03-08 16:49:21 +03:00
printf %s\\n "$rep"
2007-03-08 15:20:02 +03:00
return
fi
find-package (FindByPath): enhanced contents_index search
In FindByPath, we should always check contents_index_bin first.
It is not quite expensive, and we make no assumptions which path
entries it may contain (our contents_index_bin, in addition
to standard *bin/ paths, also has /etc entries).
However, if contents_index_bin lookup fails, we may or may
not want to proceed with very expensive contents_index_all
search. We assume that, if contents_index_bin lookup actually
has take place for some standard *bin/ path, then there is simply
no need to proceed with contents_index_all.
Now we also assume that contents_index_all file can be possibly
gzipped (I use "gzip -cdfq" for "zcat or cat", found in zgrep).
Also increased verbosity (Verbose to Info) for contents_index_all
messages, since it is expensive and it is a means of "last resort"
to finding something strange before giving up, which is quite worth
to note about.
Also increased verbosity for "raw, not found" dependencies,
since they are likely to become unmet.
2007-08-07 17:32:46 +04:00
fi
# Maybe try pkg complete index.
local idx_all="${RPM_PKG_CONTENTS_INDEX_ALL-}" try_idx_all=1
[ -n "$idx_all" ] && [ -s "$idx_all" ] && [ -r "$idx_all" ] || try_idx_all=
case "$try_idx_bin$rep" in
1/bin/* | 1/sbin/* | 1/usr/bin/* | 1/usr/sbin/* )
# Binary index already checked for standard *bin/* entries.
# No need to check complete index.
try_idx_all=
esac
if [ -n "$try_idx_all" ]; then
# Checking complete index is expensive.
2007-08-07 21:04:23 +04:00
local Verbose=Info
find-package (FindByPath): enhanced contents_index search
In FindByPath, we should always check contents_index_bin first.
It is not quite expensive, and we make no assumptions which path
entries it may contain (our contents_index_bin, in addition
to standard *bin/ paths, also has /etc entries).
However, if contents_index_bin lookup fails, we may or may
not want to proceed with very expensive contents_index_all
search. We assume that, if contents_index_bin lookup actually
has take place for some standard *bin/ path, then there is simply
no need to proceed with contents_index_all.
Now we also assume that contents_index_all file can be possibly
gzipped (I use "gzip -cdfq" for "zcat or cat", found in zgrep).
Also increased verbosity (Verbose to Info) for contents_index_all
messages, since it is expensive and it is a means of "last resort"
to finding something strange before giving up, which is quite worth
to note about.
Also increased verbosity for "raw, not found" dependencies,
since they are likely to become unmet.
2007-08-07 17:32:46 +04:00
Info "$f: checking contents_index_all for $rep"
# Complete package index is possibly gzipped.
package="$(gzip -cdfq "$idx_all" |awk -v "f=$rep" '($1 == f) {print $2}' |sort -u)"
local n="$(IFS=$'\n'; set -- $package; echo $#)"
if [ "$n" = 1 ]; then
Info "$f: $rep -> $package (via contents_index_all)"
printf %s\\n "$package"
return
elif [ "$n" -gt 1 ]; then
Info "$f: $rep indexed by:$(echo '' $package)"
Info "$f: $rep -> $rep (raw, ambiguous, via contents_index_all)"
printf %s\\n "$rep"
return
fi
fi
2003-04-22 19:11:52 +04:00
2007-03-08 15:20:02 +03:00
# Check package database.
2007-08-08 02:54:04 +04:00
if package="$(rpmquery --whatprovides --queryformat='%{NAME}\n' -- "$rep" 2>/dev/null)"; then
2007-03-08 15:20:02 +03:00
package="$(printf %s "$package" |LC_COLLATE=C sort -u)"
local n="$(IFS=$'\n'; set -- $package; echo $#)"
if [ "$n" = 1 ]; then
2007-08-07 21:04:23 +04:00
$Verbose "$f: $rep -> $package (via rpmdb)"
2007-03-08 15:20:02 +03:00
printf %s\\n "$package"
return
elif [ "$n" -gt 1 ]; then
Info "$f: $rep provided by:$(echo '' $package)"
2007-08-07 21:04:23 +04:00
Info "$f: $rep -> $rep (raw, ambiguous, via rpmdb)"
2007-03-08 16:49:21 +03:00
printf %s\\n "$rep"
return
2007-03-08 15:20:02 +03:00
fi
fi
2003-04-22 19:11:52 +04:00
2007-03-08 15:20:02 +03:00
# Not found; output raw dependence.
find-package (FindByPath): enhanced contents_index search
In FindByPath, we should always check contents_index_bin first.
It is not quite expensive, and we make no assumptions which path
entries it may contain (our contents_index_bin, in addition
to standard *bin/ paths, also has /etc entries).
However, if contents_index_bin lookup fails, we may or may
not want to proceed with very expensive contents_index_all
search. We assume that, if contents_index_bin lookup actually
has take place for some standard *bin/ path, then there is simply
no need to proceed with contents_index_all.
Now we also assume that contents_index_all file can be possibly
gzipped (I use "gzip -cdfq" for "zcat or cat", found in zgrep).
Also increased verbosity (Verbose to Info) for contents_index_all
messages, since it is expensive and it is a means of "last resort"
to finding something strange before giving up, which is quite worth
to note about.
Also increased verbosity for "raw, not found" dependencies,
since they are likely to become unmet.
2007-08-07 17:32:46 +04:00
Info "$f: $rep -> $rep (raw, not found)"
2007-03-08 15:20:02 +03:00
printf %s\\n "$rep"
}
2003-04-22 19:11:52 +04:00
2007-03-08 15:20:02 +03:00
FindByName()
{
local f="$1" r="$2" rep package; shift 2 || return
2003-04-22 19:11:52 +04:00
2007-09-19 10:31:17 +04:00
local dir="${f%/*}"; dir="${dir#${RPM_BUILD_ROOT-}}"
2007-09-18 23:46:02 +04:00
local findpackage_path="$RPM_FINDPACKAGE_USER_PATH"
2007-09-19 10:31:17 +04:00
case "$dir" in
*/sbin)
findpackage_path="$RPM_FINDPACKAGE_ROOT_PATH" ;;
/etc/*)
dir="${dir#/etc/}"; dir="${dir%%/*}"
case "$dir" in
# The sbin-ish places. WARNING: Explicit Content!
rc.d | init.d | control.d | chroot.d | net | ppp | cron* | hotplug* )
Debug "$f: root PATH on"
findpackage_path="$RPM_FINDPACKAGE_ROOT_PATH" ;;
esac
;;
2007-09-18 23:46:02 +04:00
esac
2007-09-19 10:31:17 +04:00
unset dir
2007-09-18 23:46:02 +04:00
2007-03-08 15:20:02 +03:00
# Check buildroot first.
if [ -n "${RPM_BUILD_ROOT-}" ]; then
local RPATH
2007-09-18 23:46:02 +04:00
RPATH="$(printf %s "$findpackage_path" |sed -e "s|[^:]\+|$RPM_BUILD_ROOT&|g")"
2007-03-08 15:20:02 +03:00
if rep="$(PATH="$RPATH" /usr/bin/which -- "$r" 2>/dev/null)"; then
2007-08-07 21:04:23 +04:00
$Verbose "$f: $r -> \$RPM_BUILD_ROOT${rep#$RPM_BUILD_ROOT} (skip)"
2007-03-08 15:20:02 +03:00
return
fi
fi
2003-04-22 19:11:52 +04:00
2007-03-08 15:20:02 +03:00
# Check for pkg contents binary index.
2007-08-08 00:18:21 +04:00
local save_rep= save_package=
2007-03-08 15:20:02 +03:00
if [ -n "${RPM_PKG_CONTENTS_INDEX_BIN-}" ] && [ -s "$RPM_PKG_CONTENTS_INDEX_BIN" ] && [ -r "$RPM_PKG_CONTENTS_INDEX_BIN" ]; then
2007-09-18 23:46:02 +04:00
local out="$(awk -v r="$r" -v findpackage_path="$findpackage_path" '
2007-03-08 21:54:08 +03:00
BEGIN {
2007-08-07 22:41:19 +04:00
# Here we enumerate all possible paths to keep the order;
# later we sort the result with "sort -n".
2007-09-18 23:46:02 +04:00
n = split(findpackage_path, ary, ":")
2007-03-08 21:54:08 +03:00
for (i = 1; i <= n; i++) {
dir = ary[i]
sub("/+$", "", dir)
2007-03-11 23:40:37 +03:00
file = dir "/" r
if (dir && !(file in FILES))
FILES[file] = i
2007-03-08 21:54:08 +03:00
}
2007-08-07 22:41:19 +04:00
# By now FILES is normally something like this:
# /bin/r 1
# /sbin/r 2
# /usr/bin/r 3
# /usr/sbin/r 4
# ...
2007-03-08 21:54:08 +03:00
}
2007-03-11 23:40:37 +03:00
NF==2 && ($1 in FILES) {
2007-08-07 22:41:19 +04:00
# Possible output is like this:
# 3 /usr/bin/r pkgA
# 1 /bin/r pkgB
2007-03-11 23:40:37 +03:00
print FILES[$1] "\t" $1 "\t" $2
2007-03-08 21:54:08 +03:00
}
' "$RPM_PKG_CONTENTS_INDEX_BIN" |
2007-08-07 22:59:50 +04:00
# Best paths go first:
sort -n |
# For each package, keep only the best path:
sort -u -k3 |
# Best paths still go first:
sort -n |
# Well done, discard numbers.
cut -f2-)"
2007-03-08 18:26:28 +03:00
local n="$(IFS=$'\n'; set -- $out; echo $#)"
if [ "$n" = 1 ]; then
rep="$(IFS=$'\t\n'; set -- $out; printf %s "$1")"
package="$(IFS=$'\t\n'; set -- $out; printf %s "$2")"
2007-09-12 19:28:46 +04:00
$Verbose "$f: $r -> $rep -> $package (via contents_index_bin)"
2007-03-08 18:26:28 +03:00
printf %s\\n "$package"
return
elif [ "$n" -gt 1 ]; then
2007-08-08 00:18:21 +04:00
# Content index search produced a confict: we have 2 or more paths
# from different packages. Consider this case:
# /usr/bin/r pkgA
# /usr/bin/r pkgB
# /usr/sbin/r pkgC
2007-08-10 16:08:53 +04:00
# Remember that best paths go first, and each package has only the best path.
2007-08-08 00:18:21 +04:00
# Now if the first two paths are the same, we produce raw dependency on /usr/bin/r.
2007-08-07 21:04:23 +04:00
local Verbose=Info
2007-03-08 18:26:28 +03:00
Info "$f: $r indexed by:$(printf %s "$out" |sed -e 's/\t/ -> /; s/$/,/; $s/,$//' |xargs echo '')"
rep="$(IFS=$'\t\n'; set -- $out; printf %s "$1")"
package="$(IFS=$'\t\n'; set -- $out; printf %s "$2")"
2007-08-10 16:08:53 +04:00
# Actually our contents_index generator already handles path dups,
# so the above example is likely to transform into this:
# /usr/bin/r /usr/bin/r
# /usr/sbin/r pkgC
# So we first check if path = package.
if [ "$rep" = "$package" ]; then
Info "$f: $r -> $rep -> $rep (ambiguous, via contents_index_bin)"
printf %s\\n "$rep"
return
fi
# And then we check if the first two paths are the same.
2007-03-08 18:26:28 +03:00
local rep2="$(IFS=$'\t\n'; set -- $out; printf %s "$3")"
if [ "$rep" = "$rep2" ]; then
2007-08-07 21:04:23 +04:00
Info "$f: $r -> $rep -> $rep (raw, ambiguous, via contents_index_bin)"
2007-03-08 18:26:28 +03:00
printf %s\\n "$rep"
return
2002-03-25 23:37:46 +03:00
fi
2007-08-10 16:08:53 +04:00
# However, consider yet worse real-life case:
2007-08-08 00:18:21 +04:00
# /usr/bin/arpsend arpsend
# /usr/sbin/arpsend vzctl
# In this case, we perfer to put aside the conflict for a while, and query
# the host system first. There's a good chance that the right package, either
# arpsend or vzctl, IS installed, and other unrelated packages are NOT installed.
# However, if /usr/bin/which cannot find any candidate, we have to produce the
# dependency on /usr/bin/arpsend -> arpsend.
save_rep="$rep" save_package="$package"
2007-03-08 18:26:28 +03:00
fi
2007-03-08 15:20:02 +03:00
fi
2007-03-08 16:18:41 +03:00
# Lookup in the host system.
2007-09-18 23:46:02 +04:00
if rep="$(PATH="$findpackage_path" /usr/bin/which --all -- "$r" 2>/dev/null)"; then
2007-03-08 16:18:41 +03:00
local n="$(IFS=$'\n'; set -- $rep; echo $#)"
if [ "$n" -gt 1 ]; then
2007-08-08 00:18:21 +04:00
# If '/usr/bin/which --all' yields a few paths,
# e.g. '/usr/bin/which --all awk' -> {/bin/awk,/usr/bin/awk},
# we check if all paths really point to the same file.
2007-03-08 16:18:41 +03:00
n="$(IFS=$'\n'; for f in $rep; do readlink -vm "$f"; done |sort -u |wc -l)"
2007-08-07 21:04:23 +04:00
if [ "$n" -gt 1 ]; then
local Verbose=Info
Info "$f: which $r:$(echo '' $rep)"
fi
2007-08-08 00:18:21 +04:00
# But we select the first path, which is the best, anyway.
2007-03-08 16:18:41 +03:00
rep="$(IFS=$'\n'; set -- $rep; printf %s "$1")"
fi
if [ -n "$rep" ]; then
2007-08-07 21:04:23 +04:00
$Verbose "$f: $r -> $rep -> ... (via which)"
2007-03-08 16:18:41 +03:00
FindByPath "$f" "$rep"
2007-03-08 15:20:02 +03:00
return
fi
fi
2007-08-08 00:18:21 +04:00
# Reconsult package binary index.
if [ -n "$save_rep" ] && [ -n "$save_package" ]; then
rep="$save_rep" package="$save_package"
2007-08-07 21:04:23 +04:00
$Verbose "$f: $r -> $rep -> $package (via contents_index_bin)"
2007-03-08 23:31:55 +03:00
printf %s\\n "$package"
return
fi
2007-03-08 15:20:02 +03:00
# Not found.
2007-09-19 11:08:53 +04:00
local maybe_function=
case "$r" in
*[-.]*) ;;
*[A-Z_]*) maybe_function=1 ;;
esac
if [ -n "$maybe_function" ]; then
$Verbose "$f: $r not found (skip, maybe function)"
else
Info "$f: $r not found (skip)"
fi
2007-03-08 15:20:02 +03:00
}
2007-03-06 21:08:06 +03:00
2007-03-08 15:20:02 +03:00
FindPackage()
{
local f="$1" r; shift || return
for r; do
2007-08-07 21:04:23 +04:00
local Verbose=Verbose
2007-09-10 13:26:52 +04:00
# Only these characters are allowed for pathnames or commands:
valid='A-Za-z0-9/@=.,:_+-'
2007-08-07 20:41:04 +04:00
case "$r" in
2007-09-12 20:07:43 +04:00
/*[!$valid]*)
2007-09-10 13:26:52 +04:00
Info "$f: invalid pathname: $r" ;;
2007-08-07 20:41:04 +04:00
/*)
FindByPath "$f" "$r" ;;
*/*)
2007-09-10 13:26:52 +04:00
Info "$f: invalid pathname: $r" ;;
2007-08-07 20:41:04 +04:00
-*)
2007-09-10 13:26:52 +04:00
Info "$f: invalid command: $r" ;;
2007-09-12 20:07:43 +04:00
*[!$valid]*)
2007-09-10 13:26:52 +04:00
Info "$f: invalid command: $r" ;;
2007-08-07 20:41:04 +04:00
'')
2007-09-12 19:32:59 +04:00
Verbose "$f: empty command?" ;;
2007-08-07 20:41:04 +04:00
*)
FindByName "$f" "$r" ;;
esac
2002-03-25 23:37:46 +03:00
done
}