fec29739728735f01b9377f760b992426bdc2274
[pkgusr] / usr / lib / pkgusr / install
1 #!/bin/bash
2 # Original...
3 # Copyright (c) 2000,2004 Matthias S. Benkmann <article AT winterdrache DOT de>
4 # You may do everything with this code except misrepresent its origin.
5 # PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
6
7 # Copyright (C) 2014 Steve Youngs <steve@steveyoungs.com>
8 #
9 #  Actually there's not much left of Matt's original script... pretty
10 #  much a complete re-write here. :)
11
12 ### What this is and what it does:
13 #
14 #  It is a wrapper around the install binary that comes with the
15 #  coreutils package.  It is designed to catch the most common
16 #  problems that pkgusr will face during a package install.
17 #
18 #  By far, the most common thing this script catches are setuid/gid
19 #  installs.  What we do is strip the setuid/gid bit off of the --mode
20 #  option and then go ahead with the install.  So the file is still
21 #  installed, there'll be no error, it will just not be setuid/gid.
22 #
23 #  It also sanitises the --owner and --group options.  With the former,
24 #  it changes it to the name of the pkgusr, and with the latter it
25 #  tests if the group is one of the groups that pkgusr belongs to, and
26 #  if it isn't, change the option to the pkgusr's current active group.
27 #
28 #  It only allows pkgusrs to create directories that don't already
29 #  exist.  This stops those packages that try to reset permissions on
30 #  main system directories.
31 #
32 #  It optionally suppresses installing anything into /**/share/locale.
33 #  See `## locale suppression' below for more details.
34 #
35 #  ******************************
36 #  *** S P E C I A L  N O T E ***
37 #  ******************************
38 #
39 #  Whenever this script changes something on the install command line
40 #  it is reported in the build logs.  Everything is prefixed with
41 #  '***' so always grep your logs for that after any and every package
42 #  install to see if there is anything you need to take action on.
43 #
44 ###
45
46 ### Where this differs from Matt's original script:
47 #
48 #  setuid etc: Matt's script only tested for 4755, 4775, 4711, and
49 #  nothing else.  This script tests and does something about _ALL_
50 #  file mode possibilities, including modes set via symbols instead of
51 #  octal code.
52 #
53 #  owner/group: Matt simply dropped these options if they were present.
54 #  This script tries to keep them if possible by resetting to a safer
55 #  alternative.  Both names and UIDs/GIDs are supported.
56 #
57 #  locale directories: Matt's approach was to convert any newly created
58 #  directory under /usr/share/locale to an "install" directory.  The
59 #  problem with that is the directories were not owned by root because
60 #  they were set by the pkgusr. That would allow one pkgusr to delete
61 #  another's files in that directory.
62 #
63 #  My approach to locale directories is simple... Just don't install
64 #  them.  There are three situations where they are needed...
65 #
66 #    1. You're multi-lingual and like switching between languages.
67 #    2. English is not your 1st or preferred language. (or you can't
68 #       tollerate en_US)
69 #    3. To satisfy glibc's test suite.
70 #
71 #  For me, #3 is the only one that is relevant.  But don't worry,
72 #  it is configurable via an environment variable.  You can turn on
73 #  locale installs either globally or for individual packages.  See
74 #  `## locale suppression' below and in /etc/pkgusr/bash_profile.
75 #
76 #  man pages: Matt tried to catch dups going into /usr/share/man/man?/.
77 #  I'm not bothering with this as it was too much of a "one-off" type
78 #  of occurance to have it scripted.
79 #
80 #  Running the script as root:  Matt allows it, I don't.
81 #
82 ###
83
84 ## Preserve the original command line.
85 pristinecmd=($@)
86 cmdopts=""
87
88 ## Find the real install.
89 DAISY_CHAIN=""
90 for p in $(type -ap install) ; do
91     if [ ! $p -ef $0 ]; then
92         DAISY_CHAIN=$p
93         break
94     fi
95 done
96
97 if [ ! -n "$DAISY_CHAIN" ]; then
98     echo 1>&2 '***' Cannot find real ${0##*/} command 
99     exit 1
100 fi
101
102 ## root has no business installing things here!!
103 if [ $(id -u) -eq 0 ]; then
104     echo 1>&2 '***' $(dirname $0) should not be in root\'s \$PATH
105     echo 1>&2 '***' call '"'$DAISY_CHAIN ${pristinecmd[*]}'"' directly
106     exit 1
107 fi
108
109 ## Report
110 #  When we change something, note what the original command was in the
111 #  logs.
112 report()
113 {
114         echo 1>&2 '***' install ${pristinecmd[*]}
115         return
116 }
117
118 ## locale suppression
119 #  $SUPPRESSLOCALEDIR is set in the pkgusr's environment.  It defaults
120 #  to `1' (on) which means: DO NOT install locale stuff.  To override
121 #  it set SUPPRESSLOCALEDIR=0 in the pkgusr's ~/.pkgusrrc.
122 #
123 #  For me, at least, the only package I turn off the suppression is
124 #  glibc, and that is only to satisfy glibc's test suite.
125 if [ ${SUPPRESSLOCALEDIR} -eq 1 ]; then
126     case "${pristinecmd[-1]}" in
127         (*/share/locale/*)
128         echo 1>&2 '***' Suppressed locale installation for: ${pristinecmd[-1]}
129         report
130         exit 0
131         ;;
132     esac
133 fi
134
135 ## Directories
136 #  Only allow the creation of directories that don't already exist.
137 _dirs()
138 {
139     local dir=${pristinecmd[-1]}
140
141     if [ -d ${dir} ]; then
142         report
143         exit 0
144     else
145         cmdopts="$cmdopts -d"
146     fi
147     return
148 }
149
150 ## Group
151 #  If $group is one of the groups we belong to, use it, otherwise set
152 #  -g to our currently active group and report it.
153 _group()
154 {
155     local GRP_CHAIN=""
156     local GRP_LIST
157
158     # GID or name?
159     printf '%d' "$group" &>/dev/null
160     if [ $? -eq 0 ]; then
161         GRP_LIST=$(id -G)
162     else
163         GRP_LIST=$(id -Gn)
164     fi
165
166     for g in ${GRP_LIST}; do
167         if [ $group == "$g" ]; then
168             GRP_CHAIN=$g
169             break
170         fi
171     done
172
173     if [ -z "$GRP_CHAIN" ]; then
174         report
175         cmdopts="$cmdopts -g$(id -gn)"
176     else
177         cmdopts="$cmdopts -g$group"
178     fi
179     return
180 }
181
182 ## Owner
183 #  Set -o to our username and report if it wasn't already our name.
184 _owner()
185 {
186     local MYNAME
187
188     # UID or name?
189     printf '%d' "$owner" &>/dev/null
190     if [ $? -eq 0 ]; then
191         MYNAME=$(id -u)
192     else
193         MYNAME=$(id -un)
194     fi
195
196     if [ $owner != "$MYNAME" ]; then
197         report
198     fi
199     cmdopts="$cmdopts -o$(id -un)"
200     return
201 }
202
203 ## Mode
204 #  If the file perms are being set to a 4-digit number they are
205 #  trying to do something funky like setuid.  In that case, truncate
206 #  the 1st digit and set -m to what's left, and report it.  If perms
207 #  are symbolic, convert them to numerical first.
208 _perms()
209 {
210     local testfile
211     ### HACK-O-MATIC:
212     # Convert symbolic permissions to numerical.
213     printf '%d' "$perm" &>/dev/null
214     if [ $? -ne 0 ]; then
215         testfile=$(mktemp hack-o-matic-install.XXXXX --tmpdir)
216         # A tiny risk hard-coding /bin/chmod here, but I don't won't
217         # the chmod wrapper in play for this.
218         exec /bin/chmod ${perm} ${testfile}
219         perm=$(stat --printf "%a" ${testfile})
220         rm -f ${testfile}
221     fi
222     # Catch the funky shit
223     if [ $perm -gt 999 ]; then
224         report
225         cmdopts="$cmdopts -m${perm/?/}"
226     else
227         cmdopts="$cmdopts -m$perm"
228     fi
229     return
230 }
231
232 ## Parse the command line.
233 #   All we really care about here is -d, -o, -g, and -m.  -c is
234 #   silently dropped.  Unrecognised options cause the script to exit
235 #   with a non-zero return code.
236 #
237 #   Any other legal options get passed on without alteration.
238 #
239 # NOTE: The following long options will most likely cause this script
240 #       to fail (hopefully they are obscure enough to not worry about)
241 #
242 #         --preserve-timestamps (short opt '-p' is OK)
243 #         --strip-program
244 #         --target-directory (short opt '-t' is OK)
245 #         --no-target-directory (short opt '-T' is OK)
246 #         --preserve-context
247 #
248 args=bcCdDpsTvg:m:o:S:t:Z:-:
249 while getopts $args opts; do
250     case $opts in
251         (-)
252         ## Long options
253         case $OPTARG in
254             # Passed through unaltered.
255             (backup*|compare|strip|suffix*|verbose|context*|version|help)
256                 cmdopts="$cmdopts --${OPTARG}" ;;
257             # The options we care about.
258             (directory) _dirs ;;
259             (group*) group=${OPTARG/group[=[:space:]]/}; _group ;;
260             (mode*) perm=${OPTARG/mode[=[:space:]]/}; _perms ;;
261             (owner*) owner=${OPTARG/owner[=[:space:]]/}; _owner ;;
262             # Anything else errors out.
263             (*) echo 1>&2 '***' Illegal option -- ${OPTARG}
264                 exit 1
265                 ;;
266         esac
267         ;;
268         ## Short options
269         # Passed through unaltered.
270         (b|C|D|p|s|T|v|S|t|Z) cmdopts="$cmdopts -${opts}${OPTARG}" ;;
271         # Dropped.
272         (c) ;;
273         # The options we care about.
274         (d) _dirs ;;
275         (g) group=${OPTARG}; _group ;;
276         (m) perm=${OPTARG}; _perms ;;
277         (o) owner=${OPTARG}; _owner ;;
278         # Illegal options.
279         (*) exit 1 ;;
280     esac
281 done
282 shift $(( $OPTIND - 1 ))
283
284 # We've done all we can, now lets run install
285 exec $DAISY_CHAIN ${cmdopts} $@ || exit $?
286 exit 0
287
288 ### End install
289
290 # Local variables:
291 # sh-basic-offset: 4
292 # End: