(+cc: shell people) Jonathan Nieder wrote:
> 3. If dash diverts /bin/sh, then bash's /bin/sh will be diverted > at unpack time: no file conflict. If bash diverts /bin/sh, then > dash's /bin/sh will be diverted at unpack time: still no file > conflict. But what if the sysadmin diverts /bin/sh with --local? > Won't this prevent dash from providing a diversion and cause file > conflicts? Okay, I just tried this locally, and: # dpkg-divert --divert /bin/sh.dash --add /bin/sh dpkg-divert: `local diversion of /bin/sh to /bin/sh.dash' clashes with `diversion of /bin/sh to /bin/sh.distrib by dash' So for new installations, it isn't a problem[1]. But for existing installations it is very much a problem. See <http://bugs.debian.org/538822>. Since there is no facility in dpkg for diverting multiple versions of the same file to different places, one of the two current packages that ships /bin/sh has to take responsibility for the user's custom sh. Let's say it's dash. Naive procedure. 1. Copy (or hardlink) /bin/sh to /bin/sh.local 2. Remove the local diversion! Divert --package dash /bin/sh to /bin/sh.distrib as usual. 3. (Optionally) remove /bin/sh.local if it is just a symlink to dash. 4. Let dash be unpacked, overwriting /bin/sh. 5. If dash is not configured as the system shell, copy (or hardlink) /bin/sh.local to overwrite /bin/sh in postinst. The big downside: every time dash is upgraded, there would be a window of time for these users while /bin/sh points to dash[2]. The example below is meant as pseudocode, completely untested. Thoughts? [1] except that preventing sysadmins from making /bin/sh point where they want is pretty unfriendly. [2] Longer term (squeeze + 1?) would it not be worth adopting something like the trick ajt described[3] to remove the /bin/sh link from bash and allow diversions to work as usual? [3] http://bugs.debian.org/cgi-bin/bugreport.cgi?msg=85;bug=34717 --- debian/dash.postinst | 45 ++++++++++++++++++++++++++++----------------- debian/dash.preinst | 16 +++++++++++++--- debian/dash.prerm | 31 +++++++++++++++++++++++++++---- 3 files changed, 68 insertions(+), 24 deletions(-) diff --git a/debian/dash.postinst b/debian/dash.postinst index 7e8de99..ce8a009 100644 --- a/debian/dash.postinst +++ b/debian/dash.postinst @@ -5,6 +5,7 @@ set -e check_divert() { dfile=$2; ltarget=$3 distrib=${4:-$dfile.distrib} + custom=${6:-$dfile.local} diverter=$(dpkg-divert --listpackage $dfile) truename=$(dpkg-divert --truename $dfile) case "$1" in @@ -35,22 +36,30 @@ check_divert() { fi ;; false) - if [ "$diverter" = dash ]; then - # Simple hack. We also divert sh(1), remember? - bash=$(echo "$ltarget"|sed 's/dash/bash/') - dpkg-divert --package dash --remove $dfile - # The diversion is added as if we were bash, leaving - # no diversion would lead to a file conflict later - # when re-checked by dpkg - dpkg-divert --package bash --divert $distrib --add $dfile - # ugh? $truename should always be set (to $distrib) - if [ -n "$truename" ]; then - rm -f "$truename" - fi - # Point everything back to bash - cp -dp $dfile $distrib - ln -sf $bash $dfile + if [ "$diverter" != dash ]; then + # Great. + return 0 + fi + if [ -e "$custom" ]; then + # /bin/sh.local exists. + # Use that and let dash keep the diversion. + cp -dp $custom $dfile + return 0 + fi + # Simple hack. We also divert sh(1), remember? + bash=$(echo "$ltarget"|sed 's/dash/bash/') + dpkg-divert --package dash --remove $dfile + # The diversion is added as if we were bash, leaving + # no diversion would lead to a file conflict later + # when re-checked by dpkg + dpkg-divert --package bash --divert $distrib --add $dfile + # ugh? $truename should always be set (to $distrib) + if [ -n "$truename" ]; then + rm -f "$truename" fi + # Point everything back to bash + cp -dp $dfile $distrib + ln -sf $bash $dfile ;; ash) # Code not modified by the NMU: @@ -107,7 +116,8 @@ fi if [ "$1" = configure ] && [ -z "$2" ]; then check_divert ash /bin/sh dash '' ash check_divert ash /usr/share/man/man1/sh.1.gz dash.1.gz \ - /usr/share/man/man1/sh.distrib.1.gz ash.1.gz + /usr/share/man/man1/sh.distrib.1.gz ash.1.gz \ + /usr/share/man/man1/sh.local.1.gz add_shell elif [ "$1" = configure ] && dpkg --compare-versions "$2" lt 0.4.18; then add_shell @@ -117,7 +127,8 @@ if [ $debconf ]; then db_get dash/sh check_divert "$RET" /bin/sh dash check_divert "$RET" /usr/share/man/man1/sh.1.gz dash.1.gz \ - /usr/share/man/man1/sh.distrib.1.gz + /usr/share/man/man1/sh.distrib.1.gz '' \ + /usr/share/man/man1/sh.local.1.gz fi test "$1" = 'configure' || exit 0 diff --git a/debian/dash.preinst b/debian/dash.preinst index 6553e2d..6f4afff 100644 --- a/debian/dash.preinst +++ b/debian/dash.preinst @@ -4,9 +4,18 @@ set -e divert() { dfile=$1 ltarget=$2 - div=$(dpkg-divert --list $dfile) + diverter=$(dpkg-divert --listpackage $dfile) distrib=${3:-$dfile.distrib} - if [ -z "$div" ]; then + custom=${4:-$dfile.local} + if [ "$diverter" = LOCAL ]; then + cp -ndp $dfile $custom + dpkg-divert --local --remove $dfile + if [ -h "$custom" ] && [ $(readlink "$custom") = $ltarget ]; then + rm -f $custom + fi + div= + fi + if [ -z "$diverter" ]; then dpkg-divert --package dash --divert $distrib --add $dfile # This differs from dpkg-divert's --rename because we # first make a copy of $dfile (the file being diverted) @@ -24,4 +33,5 @@ divert() { # dash: they both provide the files in the package. divert /bin/sh dash divert /usr/share/man/man1/sh.1.gz dash.1.gz \ - /usr/share/man/man1/sh.distrib.1.gz + /usr/share/man/man1/sh.distrib.1.gz \ + /usr/share/man/man1/sh.local.1.gz diff --git a/debian/dash.prerm b/debian/dash.prerm index 88a5dc2..6d458d8 100644 --- a/debian/dash.prerm +++ b/debian/dash.prerm @@ -7,10 +7,31 @@ set -e remove_divert() { - diverter=$(dpkg-divert --listpackage $1) - if [ "$diverter" = "dash" ]; then - dpkg-divert --package dash --rename --remove $1 + current=$1 + distrib=${2:-$current.distrib} + custom=${3:-$current.local} + diverter=$(dpkg-divert --listpackage $dfile) + + if [ "$diverter" != dash ]; then + return 0 + fi + if ! [ -e "$custom" ]; then + cp -db $distrib $current + dpkg-divert --package dash --remove $1 + rm -f $distrib + return 0 fi + + # Reinstate local diversion, if any. + + cp -dp $custom $current + # NEEDSWORK: dpkg-divert does not provide a + # race-free way for one package to take over + # a diversion from another. + dpkg-divert --package dash --remove $current + dpkg-divert --local --divert $distrib --add $current + rm -f $custom + return 0 } remove_shell() { @@ -23,7 +44,9 @@ remove_shell() { if [ "$1" = remove ] || [ "$1" = deconfigure ]; then remove_divert /bin/sh - remove_divert /usr/share/man/man1/sh.1.gz + remove_divert /usr/share/man/man1/sh.1.gz \ + /usr/share/man/man1/sh.distrib.1.gz \ + /usr/share/man/man1/sh.local.1.gz fi if [ "$1" = remove ]; then -- 1.7.2.3 -- To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org