2 # Copyright © 2021 Steve Youngs. All rights reserved.
3 # SPDX-License-Identifier: BSD-3-Clause
5 # Author: Steve Youngs <steve@sxemacs.org>
6 # Maintainer: Steve Youngs <steve@sxemacs.org>
7 # Created: <2021-05-04>
8 # Time-stamp: <Friday 18 Jun 2021 15:19:46 (steve)>
9 # Homepage: https://git.sxemacs.org/pkgusr
10 # Keywords: pkgusr package-management tools
12 ## This file is part of pkgusr
16 # This script outputs the contents (filelist) of a package. Along
17 # with the raw list of files and directories, there are also sectioned
18 # lists of binaries, libraries, man page summaries, and texinfo doc
19 # top node entries. If the package is holding some deprecated old
20 # libs from a previous install they are listed separately, as are any
21 # "suspicious" files/directories. By "suspicious" we mean weird-arsed
22 # permissions, broken symlinks, hardlinks, that sort of thing.
24 # The output is suitable to redirect to the pkgusr's .project file.
26 # The idea for this script comes from Matthias S. Benkmann's
27 # script 'list_package' in his set of pkgusr tools. In fact,
28 # this script uses some of Matthias' other scripts. So, to be
29 # fair, I'm going to include the copyright notice from Matthias'
30 # list_package script...
32 ###[Matthias' list_package]
33 # Copyright (c) 2004 Matthias S. Benkmann <article AT winterdrache DOT de>
34 # You may do everything with this code except misrepresent its origin.
35 # PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
38 ### User-facing differences from Matthias' list_package
41 # This script lists summaries for all man pages of a package,
42 # whereas Matt's script only lists summaries for the binaries.
43 # Matt's script lists binaries that don't have man pages (many
44 # false-positives), this script doesn't. Matt's script claims
45 # to have a bug where it can't list multiple pages that exist
46 # with the same name in different topic sections. No such bug
50 # This script -- yep; Matt's script -- nope.
53 # Matt's script supports using a group name/gid, this script
54 # doesn't. The rationale is that it was too much of a PITA to
55 # determine whether a specific group belonged to a pkgusr,
56 # especially doing so without escalating privileges. Instead
57 # this script will mark any file that has a different group
58 # from the pkgusr's primary group, but only if the script is
59 # called with '--subgroups'. (when using group name/gid with
60 # Matt's script no distinction is made in the output)
63 # Quoting part of the help output from Matt's script...
65 # | WARNING! This program is for listing files from package users only!
66 # | Do NOT use it to list files from untrusted users!
67 # | An untrusted user could set up a manipulated manpage to exploit
68 # | a bug in man when it is used to extract the summary!
70 # This script does away with that by simply refusing to run if
71 # asked to list a non-pkgusr's files.
74 # This script -- yep; Matt's script -- nope.
76 # Other than that the output is pretty much the same and all other
77 # differences are behind the scenes, hidden from the user.
82 # o Make it work without the bugs.
100 ${0##*/} PKGUSR --subgroups
102 In the first form, display this help and exit.
103 In the second, output filelist from PKGUSR package.
104 In the last, do the same, but mark any files belonging to a "sub-group"
109 ${0##*/} is a script to list an installed package's content in a
110 formatted, human-readable manner.
112 Along with a complete file list with full pathnames, it also lists
113 executable files, libraries, manpage summaries, info document
114 summaries, and any extra executables (those residing outside of
115 /**/{,s}bin/). It will then list anything odd or suspicious
116 (setuid/gid, world/group writable, sticky, broken links, hardlinks,
117 non-printable chars in filenames, etc. basically, anything funky).
118 This is done by calling out to 'list_suspicious_files_from'. Finally,
119 it lists anything from /usr/lib/deprecated (see "Deprecating
122 This script can take a very long time to complete, often several minutes,
123 and, depending on the size of the package, output many thousands of lines
124 (e.g. the output for a full KDE install is well over 60K lines). Be
125 smart, redirect to a file. Ideally ~pkgusr/.project, that way you can
126 view it later with 'pinky -l PKGUSR|less'.
131 Nothing out of the ordinary, pretty much just basic UNIX commands. This
132 script calls 'forall_direntries_from' and 'list_suspicious_files_from',
133 so anything they need which you would already have. We do need to take
134 into account dealing with compressed .info files which could be gzip,
135 bzip2, lzip, lzma, or xz (I personally have never seen any compressed
136 with anything other than gzip, but the stand-alone reader supports all
137 of those so, yeah... there is that)
139 The script will run fine if you do not have those compression tools
140 installed, it will just skip the file if it cannot be decompressed.
141 Having said that, do yourself a favour and install lzip and zutils,
142 especially zutils (and then get rid of zcat from gzip, it blows)
144 Deprecating Libraries:
145 ---------------------
147 The way I deal with library upgrades where the so version changes is
148 to put the old lib into /usr/lib/deprecated which is listed _LAST_ in
149 /etc/ld.so.conf. The way it works is...
151 /usr/lib/deprecated is obviously not in ld search path so libs in
152 there would never be used at compile/link time, but dlopen() will
153 still find them because the directory is listed in /etc/ld.so.conf.
154 Stuff that is already linked will still be able to find what it needs.
155 Then it is simply a matter of rebuilding the packages that depend on
156 the upgraded library at your leisure. Nuke the stuff in deprecated
157 once nothing needs it any longer.
163 # This interesting construct will return true if $PKG is any of:
164 # ? -? --? h -h --h help -help --help usage -usage --usage
165 [[ "$PKG" =~ ^-?-?(h(elp)?|usage|\?)$ ]] && usage
167 # Bail if $PKG isn't a real package.
168 if [[ -z ${PKG} || ! "$(id -Gn ${PKG})" =~ "install" ]]; then
169 echo 1>&2 'Invalid or missing PKGUSR'
170 echo 1>&2 Usage: ${0##*/} '[--help | PKGUSR [ --subgroups ] ]'
174 # We should definitely try to traverse the filesystem as few times as
175 # possible to save on time and CPU cycles. So lets store the complete
176 # filelist right from the get-go, but only if we need to.
177 if [ -z "${PKGASSETS[*]}" ]; then
178 # Massage $IFS to cope with filenames with spaces
181 PKGASSETS=($(forall_direntries_from $PKG|sort))
185 # We are going to want extended globbing
188 typeset -a INFOS MANS LIBS UBINS EBINS AEXEC
191 # yeah, you can use multiple counters in a for-loop
192 for ((i=0,I=0,M=0,L=0,U=0,E=0,A=0; i<${#PKGASSETS[@]}; i++)); do
193 # texinfo (collect full pathnames)
194 if [[ ${PKGASSETS[$i]} =~ /share/info/.*\.info(-[[:digit:]]+)? ]]; then
195 INFOS[$I]=${PKGASSETS[$i]}
197 # done with this file, skip to the next
200 # man (collect filenames sans extensions)
201 if [[ ${PKGASSETS[$i]} =~ /share/man/ ]]; then
202 MANS[$M]=$(basename ${PKGASSETS[$i]%%\.?(man|n|url|[[:digit:]]*)})
204 # done with this file, skip to the next
207 # libraries (collect filenames sans extensions, not symlinks)
208 if [[ ${PKGASSETS[$i]} =~ /lib(64)?/lib ]]; then
209 if [[ ! -h ${PKGASSETS[$i]} && -f ${PKGASSETS[$i]} ]]; then
210 LIBS[$L]=$(basename ${PKGASSETS[$i]%%\.?(a|so*)})
212 # add to the list of all executables (for later mining
213 # for dependencies) skip static libs
214 if [[ $(file -i ${PKGASSETS[$i]}) =~ x-sharedlib ]]; then
215 AEXEC[$A]=${PKGASSETS[$i]}
218 # done with this file, skip to the next
222 # binaries in */{,s}bin/
223 if [[ ${PKGASSETS[$i]} =~ /s?bin/ ]]; then
224 # look at symlinks first, we'll be massaging them a bit
225 if [[ -h ${PKGASSETS[$i]} && -f ${PKGASSETS[$i]} ]]; then
226 LTGT=$(stat --printf "%N" "${PKGASSETS[$i]}" |
227 awk -F\' '{print "(->"$4")"}')
228 UBINS[$U]=$(basename ${PKGASSETS[$i]})${LTGT}
230 # done with this file, skip to the next
234 if [[ -f ${PKGASSETS[$i]} && -x ${PKGASSETS[$i]} ]]; then
235 UBINS[$U]=$(basename ${PKGASSETS[$i]})
236 # add to the list of all execs (not scripts)
237 if [[ $(file -i ${PKGASSETS[$i]}) =~ x-(pie-)?(executable|sharedlib) ]]; then
238 AEXEC[$A]=${PKGASSETS[$i]}
242 # done with this file, skip to the next
246 # binaries outside of */{,s}bin/ (not symlinks)
247 if [[ ! -h ${PKGASSETS[$i]} && -f ${PKGASSETS[$i]} &&
248 -x ${PKGASSETS[$i]} ]]; then
249 EBINS[$E]=${PKGASSETS[$i]}
250 # add to the list of all execs (not scripts)
251 if [[ $(file -i ${EBINS[$E]}) =~ x-(pie-)?(executable|sharedlib) ]]; then
252 AEXEC[$A]=${EBINS[$E]}
259 # sort the libs and binaries
262 UBINS=($(sort <<<"${UBINS[*]}"))
263 LIBS=($(sort -u <<<"${LIBS[*]}"))
266 # export AEXEC for further processing
271 _have_docs=0 # Keep note if the pkg has docs
277 echo -e '\nDOCUMENTATION (texinfo):'
278 if [[ $(file -i $(type -Pp zcat)) =~ x-executable ]]; then
279 # Sweet! we have zcat from zutils
281 # This will fail if it hits a .lzma or .zst but texinfo 6.7's
282 # stand-alone info reader doesn't support zstd, and I have
283 # never in my life ever seen an info file compressed with
284 # lzma. tl;dr I'm not fixing it because you'll never see it
285 for ((i=0; i<${#INFOS[@]}; i++)); do
286 zcat ${INFOS[$i]}|sed -n /START-INFO-DIR-ENTRY/,/END-INFO-DIR-ENTRY/p |
288 done | sort -u | sed 's/^/ /'
289 else # fine, do it the hard way then
290 for ((i=0; i<${#INFOS[@]}; i++)); do
291 # Um, yeah. Slow AF. Install zutils.
292 case $(file -Lb --mime-type ${INFOS[$i]}) in
293 application/gzip) cmd=(gzip -dc) ;;
294 application/x-bzip2) cmd=(bzip2 -dc) ;;
295 application/x-xz) cmd=(xz -dc) ;;
296 application/x-lzip) cmd=(lzip -dc) ;;
297 application/x-lzma) cmd=(lzma -dc) ;;
298 application/zstd) cmd=(zstd -dc) ;;
301 type -Pp ${cmd[0]} > /dev/null || continue
302 ${cmd[@]} ${INFOS[$i]}|sed -n /START-INFO-DIR-ENTRY/,/END-INFO-DIR-ENTRY/p |
304 done | sort -u | sed 's/^/ /'
312 echo -e '\nDOCUMENTATION (man):'
313 for ((i=0; i<${#MANS[@]}; i++)); do
314 man --whatis ${MANS[$i]} 2>/dev/null
315 done | sort -u | sed 's/^/ /'
318 # We ain't got no steenkin docs
321 echo -e '\nDOCUMENTATION (lol, wut?):'
322 echo No docs here, maybe try Google...
325 ## Process the binaries
329 echo -e '\nEXECUTABLES (in */{,s}bin/):'
330 echo ${UBINS[@]}|sed -e 's/ /, /g' -e 's/^/ /'|fmt
333 # bins outside of {,s}bin
336 echo -e '\nEXTRA EXECUTABLES (outside of */{,s}bin/):'
337 for ((i=0; i<${#EBINS[@]}; i++)); do
345 echo -e '\nLIBRARIES:'
346 echo ${LIBS[@]}|sed -e 's/ /, /g' -e 's/^/ /'|fmt
349 # We need to do one more find, but it is short and sweet so won't add
350 # any noticable time to the process...
353 DEPRECATED=$(find /usr/lib/deprecated -maxdepth 1 -type f \
354 -user $PKG -printf " %p\n")
355 if [ -n "${DEPRECATED}" ]; then
356 echo -e '\nDEPRECATED LIBRARIES (remember to audit):'
361 ## The complete file list
365 echo -e '\nFILE LIST:'
366 if [[ -n ${SGRP} && ${SGRP} =~ ^--?subgroups$ ]]; then
367 for ((i=0; i<${#PKGASSETS[@]}; i++)); do
368 GRP=$(stat --printf "%G" "${PKGASSETS[$i]}")
369 if [[ ${GRP} != ${PKG} ]]; then
370 echo "* ${PKGASSETS[$i]} [${GRP}]"
372 echo " ${PKGASSETS[$i]}"
376 for ((i=0; i<${#PKGASSETS[@]}; i++)); do
377 echo " ${PKGASSETS[$i]}"
384 # binaries and libraries
385 [[ ${#UBINS[@]} -gt 0 ]] && procubins
386 [[ ${#EBINS[@]} -gt 0 ]] && procebins
387 [[ ${#LIBS[@]} -gt 0 ]] && proclibs
389 [[ ${#MANS[@]} -gt 0 ]] && procmans
390 [[ ${#INFOS[@]} -gt 0 ]] && procinfos
391 [[ ${_have_docs} -eq 0 ]] && nodocs
395 list_suspicious_files_from ${PKG}
399 ## Return cleanly via return and not exit so any calling script won't
403 ### list_package ends here