Major overhaul -- most scripts rewritten or updated.
[pkgusr] / usr / bin / list_package
1 #!/bin/bash
2 # Copyright (c) 2004 Matthias S. Benkmann <article AT winterdrache DOT de>
3 # You may do everything with this code except misrepresent its origin.
4 # PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
5
6 if [ $# -lt 1 -o \( $# -gt 1 -a "z$1" != "z:man" -a "z$1" != "z:mani" -a "z$1" != "z:lib" \) -o "$1" = "--help" ]; then
7     echo 1>&2
8     echo 1>&2 'USAGE: '"${0##*/}"' <user_or_group_name>'
9     echo 1>&2
10     echo 1>&2 '  Entries will be matched if group and/or user equals <user_or_group_name>'
11     echo 1>&2 '    (numeric UID/GID allowed).'
12     echo 1>&2 '  This script uses `forall_direntries_from'"'"' and `list_suspicious_files_from'"'."
13     echo 1>&2
14     echo 1>&2 '  NOTE: Several minutes may pass before you see the first output.'
15     echo 1>&2 '  You should probably redirect output to a file for later reference.'
16     echo 1>&2
17     echo 1>&2 '  WARNING! This program is for listing files from package users only!'
18     echo 1>&2 '           Do NOT use it to list files from untrusted users!'
19     echo 1>&2 '           An untrusted user could set up a manipulated manpage to exploit'
20     echo 1>&2 '           a bug in man when it is used to extract the summary!'
21     exit 1
22 fi
23
24 # KNOWN BUGS:
25 #  - when extracting summaries from manpages, candidate manpages are considered
26 #    in alphabetic order rather than the order used by the man command.
27 #    The problem with this is that section 8, which contains manpages for
28 #    admin commands, will be considered after lower-numbered sections.
29 #    In the rare case that an admin command has the same name as a topic from
30 #    a lower-numbered manpage installed by the same package, the summary will
31 #    be taken from the wrong manpage.
32 #    An example for such a clash are the faillog.5 and faillog.8 manpages from
33 #    the shadow package.
34 #    Because this problem is difficult to fix, rare and easily spotted (since
35 #    the manpage that should have provided the summary will be listed under
36 #    EXTRA MANPAGES) I won't fix it.
37
38 ugname="$1"
39
40 #suppress ugly debug output from shell
41 trap ':' SIGPIPE
42
43 if [ $# -gt 1 ]; then
44     name="${2##*/}"
45     case "$1" in
46         :man) 
47             name=${name%.gz}
48             name=${name%.bz2}
49             name=${name%.*}
50             echo $'command\2'"$name"$'\2man\2'"$2" ;;
51         :mani) 
52             name=${name%.gz}
53             name=${name%.bz2}
54             name=${name%.*}
55             echo $'command\2'"$name"$'\2mani\2'"$2" ;;
56         :lib) 
57             name=${name%.a}
58             name=${name%%.so*}
59             echo "lib $name" 
60             ;;
61     esac
62     exit 0
63 fi
64
65 sanitize() { tr -c '[:print:]' '?' ; }
66
67 # $1: <commandname>
68 # $2: command\2<commandname>\2cmd\2(-><linktarget>)
69 # $3: command\2<commandname>\2man[i]\2<manpage_path>  or  <empty>
70 expand_command()
71 {
72     sep=$'\2'
73     cmdname="$1"
74     cmdline="$2"
75     manline="$3"
76     linktarget="${cmdline##*${sep}}"
77   
78     if [ -z "$manline" ]; then
79         description='No manpage'
80         #the "l" at the beginning is just to make it sort after "lib"
81         echo -n "lmanlessbin $cmdname" | sanitize
82         echo 
83     
84     else # if [ ! -z "$manline" ]; then
85         manpage=${manline##*${sep}}
86         wsc='[[:space:],]\+'
87         # The `t l;d;:l;n;b l' in the sed command is voodoo magic to make sed
88         # output only the first match but to keep eating up all input. I use this
89         # instead of  `| head -n 1', because head breaks the pipe after doing
90         # its 1 line output, which (if it happens before sed has processed the
91         # complete input) freaks out man and causes it to emit a totally
92         # silly error message including "No such file or directory", which is
93         # annoying when you do testing without suppressing man's errors.
94         # The $'s/.\b\\(.\\)/\\1/g;s/\x1b[^m]\\+m//g' removes the backspace-based 
95         # as well as ESC-based formatting from man's output. 
96         description="$( { COLUMNS=300 man "$manpage" 2>/dev/null || 
97                                       echo " $name - Broken manpage" ; } | 
98         sed $'s/.\b\\(.\\)/\\1/g;s/\x1b[^m]\\+m//g' |
99         sed -n "/^NAME/,/^[A-Z]/s/^.*${wsc}${cmdname}${wsc}.*-\+${wsc}\(.*\)/\1/p;t l;d;:l;n;b l" )"
100         if [ -z "$description" ]; then
101             description="$( COLUMNS=300 man "$manpage" 2>/dev/null |
102             sed $'s/.\b\\(.\\)/\\1/g;s/\x1b[^m]\\+m//g' |
103             sed -n "s/^.*${wsc}..*${wsc}.*-\+${wsc}\(.*\)/\1/p;t l;d;:l;n;b l" )"
104         fi
105         test -z "$description" && description="Weird manpage"
106     fi
107
108     echo -n "binexe $cmdname" | sanitize
109     test "$linktarget" != '(->)' && echo -n "$linktarget" | sanitize
110     echo 
111     #the "lx" in "lxdescription" is just to make sure it sorts after "lmanlessbin"
112     echo -n "lxdescription $cmdname: $description" | sanitize  
113     echo 
114 }
115
116 # NOTE: The -path and -lname stuff at the beginning of the following is
117 # there to make sure that none of the lines output by find contains
118 # a) \n or \r, because that would mess up post-processing the output 
119 #    line-by-line.
120 # b) \x7f, because this character triggers one of the nastier bash-bugs
121 #    wrt string handling
122 # c) \2, because I use this as separator within the lines
123 #    (Why \2 and not \0 or \1 ? Because bash can't cope with \0 at all and has
124 #    bugs related to \1.)
125 #
126 # Because of this, having the final section called "ALL FILES" is technically
127 # a lie, because files with a path containing one of the abovementioned
128 # characters will not appear in output.
129 # However, a) no sane package contains such files
130 #          b) they will be listed in the output from list_suspicious_files
131 cmd=(\( -path $'*\n*' -or -path $'*\r*' -or -path $'*\x7f*' 
132         -or -path $'*\2*'
133         -or -lname $'*\n*' -or -lname $'*\r*' -or -lname $'*\x7f*' 
134         -or -lname $'*\2*' 
135      \) 
136      -or
137      \(
138        \( -printf "zall %p\n" \) ,
139        \(
140          \( -type f -or -xtype f \) -and
141          \(
142            \( -perm -u+x \( -path "*/bin/*" -or -path "*/sbin/*" \) -printf 'command\2%f\2cmd\2(->%l)\n' \)
143        -or \( -path "*/man/man*/*" -exec "$0" ":man" {} \; \)
144        -or \( -path "*/man/*/man*/*" -exec "$0" ":mani" {} \; \)
145        -or \( \( -name "lib*.so" -or -name "lib*.a" -or -name "lib*.so.*" \) -path "*/lib/*" -exec "$0" ":lib" {} \; \)
146        -or \( -type f -perm -u+x -not \( \( -name "lib*.so" -or -name "lib*.a" -or -name "lib*.so.*" \) -path "*/lib/*" \)  -printf "nobinexe %p\n" \)
147          \)
148        \)  
149      \)
150     )
151
152 forall_direntries_from "$ugname" "${cmd[@]}" | sort -u |
153 {
154     sep=$'\2'
155     hold=''
156     for((;;)); do
157         if [ -z "$hold" ]; then
158             read -r line || break
159         else
160             line="$hold"
161             hold=''
162         fi
163     
164         case "z$line" in
165             zcommand${sep}*${sep}cmd${sep}*) 
166             cmdname=${line#command${sep}}
167             cmdname=${cmdname%%${sep}*}
168             read -r hold
169                 case "z$hold" in
170                     zcommand${sep}${cmdname}${sep}man${sep}*|zcommand${sep}${cmdname}${sep}mani${sep}*) 
171                     expand_command "$cmdname" "$line" "$hold"
172                     hold=''
173                     ;;
174           
175                     z*) 
176                         expand_command "$cmdname" "$line" ""
177                         ;;
178                 esac
179                 ;;
180
181                 zcommand${sep}*${sep}man${sep}*|command${sep}*${sep}mani${sep}*) 
182          
183                 echo -n "manextra ${line##*${sep}}" | sanitize
184                 echo
185                 ;;
186
187             z*) 
188                 echo -n "$line" | sanitize
189                 echo
190                 ;;
191         esac
192     
193   done
194 } | sort |  #no -u here, bc. the above processing may equalize different files
195 {
196 # (1) binexe: Executables (in *bin/)
197 # (2) lib: Libraries (in *lib/*)
198 # (3) lmanlessbin: Executables (in *bin/) without manpages
199 # (4) lxdescription: Summaries for executables (in *bin/)
200 # (5) manextra: Extra manpages
201 #     full paths, no perms
202 # (6) nobinexe: Executables not in *bin/ (excluding *lib/*.so and *lib/*.so.*)
203 #     full paths, no perms
204 # (7) zall: All files
205 #     full paths, no perms
206
207     curstate=''
208     while read -r line ; do
209         newstate="${line%% *}"
210         if [ "$newstate" != "$curstate" ]; then
211             curstate="$newstate"
212             case "$curstate" in
213                 binexe)
214                     echo 'EXECUTABLES (in */bin or */sbin)'
215                     echo -n "  ${line#binexe }"
216                     ;;
217                 lib)
218                     echo
219                     echo
220                     echo 'LIBRARIES (lib*.a or lib*.so)'
221                     echo -n "  ${line#lib }"
222                     ;;
223                 lmanlessbin)
224                     echo
225                     echo
226                     echo 'EXECUTABLES WITH NO MANPAGE (in */bin or */sbin)'
227                     echo -n "  ${line#lmanlessbin }"
228                     ;;
229                 lxdescription)
230                     echo
231                     echo
232                     echo 'MANPAGE SUMMARIES OF EXECUTABLES (in */bin or */sbin)'
233                     echo "  ${line#lxdescription }"
234                     ;;
235                 manextra)
236                     echo
237                     echo 'EXTRA MANPAGES'
238                     echo "  ${line#manextra }"
239                     ;;
240                 nobinexe)
241                     echo
242                     echo 'EXTRA EXECUTABLES (not in */bin or */sbin)'
243                     echo "  ${line#nobinexe }"
244                     ;;
245                 zall)
246                     echo
247                     echo 'ALL FILES'
248                     echo "  ${line#zall }"
249                     ;;
250                 *) 
251                     echo
252                     echo
253                     echo 'UNEXPECTED LINE'
254                     echo "  $line"
255                     ;;
256             esac
257         else
258             case "$curstate" in
259                 binexe) echo -n ", ${line#binexe }" ;;
260                 lib) echo -n ", ${line#lib }" ;;
261                 lmanlessbin) echo -n ", ${line#lmanlessbin }" ;;
262                 lxdescription) echo "  ${line#lxdescription }" ;;
263                 manextra) echo "  ${line#manextra }" ;;
264                 nobinexe) echo "  ${line#nobinexe }" ;;
265                 zall) echo "  ${line#zall }" ;;
266                 *) 
267                     echo
268                     echo 'UNEXPECTED LINE'
269                     echo "  $line"
270                     ;;
271             esac
272         fi
273     done
274
275
276 list_suspicious_files_from "$ugname"