#!/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
-if [ $UID == 0 ]; then
- exec $DAISY_CHAIN "$@"
+## root has no business installing things here!!
+if [ $(id -u) -eq 0 ]; then
+ 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 dir=${pristinecmd[-1]}
+
+ if [ -d ${dir} ]; then
+ report
+ exit 0
+ 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?
+ printf '%d' "$group" &>/dev/null
+ if [ $? -eq 0 ]; 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 -o to our username and report if it wasn't already our name.
+_owner()
+{
+ local MYNAME
+
+ # UID or name?
+ printf '%d' "$owner" &>/dev/null
+ if [ $? -eq 0 ]; then
+ MYNAME=$(id -u)
+ else
+ MYNAME=$(id -un)
+ fi
+
+ if [ $owner != "$MYNAME" ]; then
+ report
+ fi
+ cmdopts="$cmdopts -o$(id -un)"
+ return
+}
+
+## Mode
+# Remove any nasty bits like setuid, setgid, sticky.
+_perms()
+{
+ local tperm=${perm}
+ printf '%o' "0${tperm}" &>/dev/null
+ if [ $? -ne 0 ]; then
+ tperm=${tperm//[st]/}
+ else
+ if [ ${tperm} -gt 777 ]; then
+ tperm=${tperm/?/}
+ fi
+ fi
+ # Did we change anything?
+ if [ "${tperm}" != "${perm}" ]; then
+ report
+ cmdopts="$cmdopts -m$tperm"
+ else
+ cmdopts="$cmdopts -m$perm"
+ fi
+ return
+}
+
+## Parse the command line.
+# All we really care about here is -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 options
+ case $OPTARG in
+ # Passed through unaltered.
+ (backup*|compare|strip|suffix*|verbose|context*|version|help)
+ cmdopts="$cmdopts --${OPTARG}" ;;
+ # The options we care about.
+ (directory) _dirs ;;
+ (group*) group=${OPTARG/group[=[:space:]]/}; _group ;;
+ (mode*) perm=${OPTARG/mode[=[:space:]]/}; _perms ;;
+ (owner*) owner=${OPTARG/owner[=[:space:]]/}; _owner ;;
+ # Anything else errors out.
+ (*) echo 1>&2 '***' Illegal option -- ${OPTARG}
+ exit 1
+ ;;
+ esac
+ ;;
+ ## Short options
+ # Passed through unaltered.
+ (b|C|D|p|s|T|v|S|t|Z) cmdopts="$cmdopts -${opts}${OPTARG}" ;;
+ # Dropped.
+ (c) ;;
+ # The options we care about.
+ (d) _dirs ;;
+ (g) group=${OPTARG}; _group ;;
+ (m) perm=${OPTARG}; _perms ;;
+ (o) owner=${OPTARG}; _owner ;;
+ # Illegal options.
+ (*) 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: