#!/bin/bash
+# Original...
# Copyright (c) 2000,2004 Matthias S. Benkmann <article AT winterdrache DOT de>
# You may do everything with this code except misrepresent its origin.
# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
-manpagesowner=man-pages
-localedir=/usr/share/locale
-cmdline="$@"
+# Copyright (C) 2014 Steve Youngs <steve@steveyoungs.com>
+#
+# Actually there's not much left of Matt's original script... pretty
+# much a complete re-write here. :)
-DAISY_CHAIN=""
+### What this is and what it does:
+#
+# It is a wrapper around the install binary that comes with the
+# coreutils package. It is designed to catch the most common
+# problems that pkgusr will face during a package install.
+#
+# By far, the most common thing this script catches are setuid/gid
+# installs. What we do is strip the setuid/gid bit off of the --mode
+# option and then go ahead with the install. So the file is still
+# installed, there'll be no error, it will just not be setuid/gid.
+#
+# It also sanitises the --owner and --group options. With the former,
+# it changes it to the name of the pkgusr, and with the latter it
+# tests if the group is one of the groups that pkgusr belongs to, and
+# if it isn't, change the option to the pkgusr's current active group.
+#
+# It only allows pkgusrs to create directories that don't already
+# exist. This stops those packages that try to reset permissions on
+# main system directories.
+#
+# It optionally suppresses installing anything into /**/share/locale.
+# See `## locale suppression' below for more details.
+#
+# ******************************
+# *** S P E C I A L N O T E ***
+# ******************************
+#
+# Whenever this script changes something on the install command line
+# it is reported in the build logs. Everything is prefixed with
+# '***' so always grep your logs for that after any and every package
+# install to see if there is anything you need to take action on.
+#
+###
+
+### Where this differs from Matt's original script:
+#
+# setuid etc: Matt's script only tested for 4755, 4775, 4711, and
+# nothing else. This script tests and does something about _ALL_
+# file mode possibilities, including modes set via symbols instead of
+# octal code.
+#
+# owner/group: Matt simply dropped these options if they were present.
+# This script tries to keep them if possible by resetting to a safer
+# alternative. Both names and UIDs/GIDs are supported.
+#
+# locale directories: Matt's approach was to convert any newly created
+# directory under /usr/share/locale to an "install" directory. The
+# problem with that is the directories were not owned by root because
+# they were set by the pkgusr. That would allow one pkgusr to delete
+# another's files in that directory.
+#
+# My approach to locale directories is simple... Just don't install
+# them. There are three situations where they are needed...
+#
+# 1. You're multi-lingual and like switching between languages.
+# 2. English is not your 1st or preferred language. (or you can't
+# tollerate en_US)
+# 3. To satisfy glibc's test suite.
+#
+# For me, #3 is the only one that is relevant. But don't worry,
+# it is configurable via an environment variable. You can turn on
+# locale installs either globally or for individual packages. See
+# `## locale suppression' below and in /etc/pkgusr/bash_profile.
+#
+# man pages: Matt tried to catch dups going into /usr/share/man/man?/.
+# I'm not bothering with this as it was too much of a "one-off" type
+# of occurance to have it scripted.
+#
+# Running the script as root: Matt allows it, I don't.
+#
+###
+
+## Preserve the original command line.
+pristinecmd=($@)
+cmdopts=""
+## Find the real install.
+DAISY_CHAIN=""
for p in $(type -ap install) ; do
if [ ! $p -ef $0 ]; then
DAISY_CHAIN=$p
done
if [ ! -n "$DAISY_CHAIN" ]; then
- echo Cannot find real ${0##*/} command
+ echo 1>&2 '***' Cannot find real ${0##*/} command
exit 1
fi
+## root has no business installing things here!!
if [ $UID == 0 ]; then
- exec $DAISY_CHAIN "$@"
+ echo 1>&2 '***' $(dirname $0) should not be in root\'s \$PATH
+ echo 1>&2 '***' call '"'$DAISY_CHAIN ${pristinecmd[*]'"' directly
+ exit 1
fi
-#kill unused -c parameter if we get it
-if [ z"$1" = z"-c" ]; then shift 1 ; fi
-
- #********** test if we create directories ********************
-if [ \( z"$1" = z"-d" \) -o \( z"$1" = z"-m" -a z"$3" = z"-d" \) ]; then
- locdirs=""
- notify=0
- havedir=0
- for((i=$#; $i>0; ))
- do
- a="$1"
- shift 1; i=$(($i-1))
- case "$a" in
- -o|-g|--owner|--group)
- notify=1
- shift 1; i=$(($i-1))
- set -- "$@"
- ;;
- $localedir/*)
- if [ ! -d "$a" ]; then
- locdirs="$locdirs ""$(expr $a : "$localedir/\(.*\)")"
- set -- "$@" "$a"
- havedir=1
- else
- notify=1
- set -- "$@"
- fi
- ;;
- */*|/sbin)
- if [ ! -d "$a" ]; then
- set -- "$@" "$a"
- havedir=1
- else
- notify=1
- set -- "$@"
- fi
- ;;
- *) set -- "$@" "$a" ;;
- esac
- done
-
- test $notify -eq 1 -o z"$locdirs" != z && \
- echo 1>&2 '***' install "$cmdline"
-
- test $havedir -eq 0 && exit 0
-
- $DAISY_CHAIN "$@" || exit $?
-
- test z"$locdirs" != z &&
- for dir in $locdirs ; do
- cumuldir=""
- for d in $(echo $locdirs | sed 's#/# #g' -) ; do
- cumuldir=$cumuldir$d/
- if [ -d $localedir/$cumuldir ]; then
- chgrp install $localedir/$cumuldir
- chmod g+w,o+t $localedir/$cumuldir
- fi
- done
- done
+## Report
+# When we change something, note what the original command was in the
+# logs.
+report()
+{
+ echo 1>&2 '***' install ${pristinecmd[*]}
+ return
+}
-else #if "$1" != "-d" ,i.e. we do not create directories *****************
- notify=0
- for((i=$# ; $i>0; ))
- do
- a="$1"
- shift 1; i=$(($i-1))
- case "$a" in
- -m)
- set -- "$@" "$a"
- a="$1"
- shift 1; i=$(($i-1))
- case "$a" in
- 4755) notify=1 ; set -- "$@" "755" ;;
- 4775) notify=1 ; set -- "$@" "755" ;;
- 4711) notify=1 ; set -- "$@" "711" ;;
- *) set -- "$@" "$a" ;;
- esac
- ;;
- -m4755) notify=1 ; set -- "$@" "-m755" ;;
- -m4775) notify=1 ; set -- "$@" "-m755" ;;
- -m4711) notify=1 ; set -- "$@" "-m711" ;;
- -o|-g|--owner|--group)
- notify=1
- shift 1; i=$(($i-1))
- set -- "$@"
- ;;
- */man/man?/*)
- if [ -e "$a" -a ! -O "$a" ]; then
- if [ $(find "$a" -printf \%u) = $manpagesowner ]; then
- notify=1
- set -- "$@" not_installed
- else
- set -- "$@" "$a"
- fi
- else
- set -- "$@" "$a"
- fi
- ;;
- *) set -- "$@" "$a" ;;
- esac
+## locale suppression
+# $SUPPRESSLOCALEDIR is set in the pkgusr's environment. It defaults
+# to `1' (on) which means: DO NOT install locale stuff. To override
+# it set SUPPRESSLOCALEDIR=0 in the pkgusr's ~/.pkgusrrc.
+#
+# For me, at least, the only package I turn off the suppression is
+# glibc, and that is only to satisfy glibc's test suite.
+if [ ${SUPPRESSLOCALEDIR} -eq 1 ]; then
+ case "${pristinecmd[-1]}" in
+ (*/share/locale/*)
+ echo 1>&2 '***' Suppressed locale installation for: ${pristinecmd[-1]}
+ report
+ exit 0
+ ;;
+ esac
+fi
+
+## Directories
+# Only allow the creation of directories that don't already exist.
+_dirs()
+{
+ local myarray=($@)
+ local dir=${myarray[-1]}
+
+ if [ -d ${dir} ]; then
+ report
+ else
+ cmdopts="$cmdopts -d"
+ fi
+ return
+}
+
+## Leading directories (install option -D)
+# The same as for -d, only create if it doesn't exist. But we need
+# to do a little more this time because if we decide to NOT create
+# the directory we will need to alter the command line further...
+#
+# install -Dm755 myprog /usr/bin/myprog
+# To
+# install -m755 myprog /usr/bin
+_leading_dirs()
+{
+ local myarray=($@)
+ local dir=$(dirname ${myarray[-1]})
+ local last=$((${#myarray[*]}-1))
+
+ if [ -d ${dir} ]; then
+ report
+ myarray[${last}]=${dir}
+ set --
+ set -- "$@" "${myarray[*]}"
+ else
+ cmdopts="$cmdopts -D"
+ fi
+ return
+}
+
+## Group
+# If $group is one of the groups we belong to, use it, otherwise set
+# -g to our currently active group and report it.
+_group()
+{
+ local GRP_CHAIN=""
+ local GRP_LIST
+
+ # GID or name?
+ if [ $group -ge 0 2>/dev/null ]; then
+ GRP_LIST=$(id -G)
+ else
+ GRP_LIST=$(id -Gn)
+ fi
+
+ for g in ${GRP_LIST}; do
+ if [ $group == "$g" ]; then
+ GRP_CHAIN=$g
+ break
+ fi
done
- test $notify -eq 1 && echo 1>&2 '***' install "$cmdline"
+ if [ -z "$GRP_CHAIN" ]; then
+ report
+ cmdopts="$cmdopts -g$(id -gn)"
+ else
+ cmdopts="$cmdopts -g$group"
+ fi
+ return
+}
- $DAISY_CHAIN "$@" || exit $?
-fi
+## Owner
+# Set -u to our username and report if it wasn't already our name.
+_owner()
+{
+ local MYNAME
+
+ # UID or name?
+ if [ $owner -ge 0 2>/dev/null ]; then
+ MYNAME=$(id -u)
+ else
+ MYNAME=$(id -un)
+ fi
+
+ if [ $owner != "$MYNAME" ]; then
+ report
+ fi
+ cmdopts="$cmdopts -u$(id -un)"
+ return
+}
+## Mode
+# If the file perms are being set to a 4-digit number they are
+# trying to do something funky like setuid. In that case, truncate
+# the 1st digit and set -m to what's left, and report it. If perms
+# are symbolic, convert them to numerical first.
+_perms()
+{
+ local p
+ ### HACK-O-MATIC:
+ # Convert symbolic permissions to numerical. This works via
+ # side-effect because if $perm is text it causes the if to error
+ # and you come out on the else branch.
+ if [ $perm -ge 0 2>/dev/null ]; then
+ p=$perm
+ else
+ touch /tmp/hack-o-matic-9000
+ chmod $perm /tmp/hack-o-matic-9000
+ p=$(stat --printf "%a" /tmp/hack-o-matic-9000)
+ rm -f /tmp/hack-o-matic-9000
+ fi
+ # Catch the funky shit
+ if [ $p -gt 999 ]; then
+ report
+ cmdopts="$cmdopts -m${p/?/}"
+ else
+ cmdopts="$cmdopts -m$p"
+ fi
+ return
+}
+
+## Parse the command line.
+# All we really care about here is -d, -D, -o, -g, and -m. -c is
+# silently dropped. Unrecognised options cause the script to exit
+# with a non-zero return code.
+#
+# Any other legal options get passed on without alteration.
+#
+# NOTE: The following long options will most likely cause this script
+# to fail (hopefully they are obscure enough to not worry about)
+#
+# --preserve-timestamps (short opt '-p' is OK)
+# --strip-program
+# --target-directory (short opt '-t' is OK)
+# --no-target-directory (short opt '-T' is OK)
+# --preserve-context
+#
+args=bcCdDpsTvg:m:o:S:t:Z:-:
+while getopts $args opts; do
+ case $opts in
+ (-)
+ # long opts
+ case $OPTARG in
+ (backup*) cmdopts="$cmdopts --${OPTARG}" ;;
+ (compare) cmdopts="$cmdopts -C" ;;
+ (directory) _dirs ;;
+ (group*) group=${OPTARG/group[=[:space:]]/}; _group ;;
+ (mode*) perm=${OPTARG/mode[=[:space:]]/}; _perms ;;
+ (owner*) owner=${OPTARG/owner[=[:space:]]/}; _owner ;;
+ (strip) cmdopts="$cmdopts -s" ;;
+ (suffix*) cmdopts="$cmdopts -S${OPTARG/suffix[=[:space:]]/}" ;;
+ (verbose) cmdopts="$cmdopts -v" ;;
+ (context*) cmdopts="$cmdopts -Z${OPTARG/context[=[:space:]]/}" ;;
+ (help) cmdopts="$cmdopts --help" ;;
+ (version) cmdopts="$cmdopts --version" ;;
+ (*) echo 1>&2 '***' Illegal option -- ${OPTARG}
+ exit 1
+ ;;
+ esac
+ ;;
+ # short opts
+ (b) cmdopts="$cmdopts -b" ;;
+ (C) cmdopts="$cmdopts -C" ;;
+ (c) ;; # no-op
+ (d) _dirs ;;
+ (D) _leading_dirs ;;
+ (g) group=${OPTARG}; _group ;;
+ (m) perm=${OPTARG}; _perms ;;
+ (o) owner=${OPTARG}; _owner ;;
+ (p) cmdopts="$cmdopts -p" ;;
+ (s) cmdopts="$cmdopts -s" ;;
+ (S) cmdopts="$cmdopts -S${OPTARG}" ;;
+ (t) cmdopts="$cmdopts -t${OPTARG}" ;;
+ (T) cmdopts="$cmdopts -T" ;;
+ (v) cmdopts="$cmdopts -v" ;;
+ (Z) cmdopts="$cmdopts -Z${OPTARG}" ;;
+ (*) exit 1 ;;
+ esac
+done
+shift $(( $OPTIND - 1 ))
+
+# We've done all we can, now lets run install
+$DAISY_CHAIN "$cmdopts $@" || exit $?
exit 0
+
+### End install
+
+# Local variables:
+# sh-basic-offset: 4
+# End: