(+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

Reply via email to