[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Patch for bash



severity 34717 wishlist
thanks

Severity rationale: the current behaviour is bug free, and works. Having
/bin/sh point to ash is not a release critical feature. A workaround exists:
use:
	# dpkg-divert --rename --add /bin/sh
	# ln -sf ash /bin/sh
to get the desired behaviour without changing bash.

But read on anyway...

On Sat, Aug 28, 1999 at 09:18:17AM -0400, Michael Stone wrote:
> On Sat, Aug 28, 1999 at 02:10:01PM +1000, Anthony Towns wrote:
> > Note that the first maintainer script after "remove /bin/sh" is the
> > postinst -- adding the link in the preinst doesn't do us any good on
> > upgrades.
> Ok. I stand corrected. How's this for a nasty hack: what if the preinst
> diverts /bin/sh before cretaing the symlink?

You're one sick individual.

The following seems to work: (consider it pseudocode, however)

preinst:
	#!/bin/sh
	set -e

	dpkg --assert-support-predepends || (echo "new dpkg"; exit 1)

	# are we upgrading from a version of bash that includes /bin/sh in
	# the package? If so, divert it so it doesn't get removed when
	#  unpacking.
	#
	# NB: dpkg-divert seems to get confused if a package diverts one of its
	# own files, so we use a pseudo package of "bash-preinst" here. Note
	# that this diversion gets removed in the postinst.

	if [ "$1" = "upgrade" ]; then
		if dpkg --compare-versions "$2" lt 2.02.1-1.7; then
			/usr/sbin/dpkg-divert --package bash-preinst \
				--add --rename --divert /bin/sh.bash-old \
				--quiet /bin/sh || (
			  echo "couldn't divert."; exit 1
			)
		fi
	fi

	# We've ensured any /bin/sh link won't be rm'ed after unpacking -- so
	# we can make a link here to ensure /bin/sh exists even if bash is only
	# unpacked and not configured.

	if [ -e /bin/sh ]; then
		true
	else
		echo "Adding symlink"
		ln -s bash /bin/sh
	fi

postinst:
	#!/bin/bash
	set -e

	# if the diversion still exists, get rid of it -- it's only necessary
	# while bash is unconfigured
	dpkg-divert --quiet --package bash-preinst --remove /bin/sh

	install-info --quiet --description="GNU Bourne-Again SHell Features." \
	 --section "General Commands" "General Commands" /usr/info/bash.info.gz

Now for the longer version.

First, the preinst in C:

----bash.preinst.c----
/* Copyright (c) 1999 Anthony Towns
 *
 * You may freely use, distribute, and modify this program.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>

int mysystem(char *filename, ...) {
    char *args[100];
    int i = 0;
    va_list ap;
    pid_t child;

    args[0] = filename;
    va_start(ap, filename);
    while(++i < 100 && (args[i] = va_arg(ap, char*)))
        ;
    args[99] = NULL;

    switch(child = fork()) {
      case -1:
        /* fork failed */
        return 127;

      case 0: 
        /* i'm the child */
        {
            execv(filename, args);
	    return 127;
        }

      default:
        /* i'm the parent */
        {
            int status;
            pid_t pid;
            pid = wait(&status);
            if (pid == child) {
                if (WIFEXITED(status)) {
                    return WEXITSTATUS(status);
                }
            }
            return 127;
        }
    }
}

int dpkg_ver(char *left, char *right) {
    int result;

    if (strcmp(left, right) == 0) return 0;

    result = mysystem("/usr/bin/dpkg", "--compare-versions",
                      left, "lt", right, NULL);

    if (result == 0) return -1;
    if (result == 1) return 1;

    /* otherwise there's been a problem. */
    return 0;
}

int main(int argc, char **argv) {
    struct stat stat_buf;

    if (mysystem("/usr/bin/dpkg", "--assert-support-predepends", NULL) != 0) {
        printf("\nPlease upgrade to a new version of dpkg\n\n");
        return EXIT_FAILURE;
    }

    /* are we upgrading from a version of bash that includes /bin/sh in
     * the package? If so, divert it so it doesn't get removed when unpacking.
     *
     * NB: dpkg-divert seems to get confused if a package diverts one of its
     * own files, so we use a pseudo package of "bash-preinst" here. Note that
     * this diversion gets removed in the postinst.
     */

    if (argc >= 2 && strcmp(argv[1],"upgrade") == 0) {
        if (argc == 2 || dpkg_ver(argv[2], "2.02.1-1.7") < 0) {
            if (mysystem("/usr/sbin/dpkg-divert", "--package", "bash-preinst",
                         "--add", "--rename", "--divert", "/bin/sh.bash-old",
                         "--quiet", "/bin/sh", NULL) != 0)
            {
		printf("/bin/sh: Couldn't ensure /bin/sh won't be lost "
                       "during upgrade; aborting\n");
		return EXIT_FAILURE;
            }
        }
    }

    /* /bin/sh link won't be rm'ed after unpacking -- so we can make a link
     * to ensure /bin/sh exists even if bash is only unpacked and not
     * configured.
     */

    if (lstat("/bin/sh", &stat_buf) == 0 || errno != ENOENT) {
        /* /bin/sh already exists; presume the admin knows what ey's doing,
         * and leave it as is.
         */
    } else {
        printf("Adding symlink /bin/sh to /bin/bash.\n");
        if (symlink("/bin/bash", "/bin/sh") < 0) {
            perror("/bin/sh");
            return EXIT_FAILURE;
        }
    }

    return EXIT_SUCCESS;
}
----bash.preinst.c----

Next, some demonstration that it actually works.

First, when Apt decides to remove and reinstall bash:

====REMOVAL AND REINSTALLATION====
Script started on Mon Aug 30 16:03:06 1999
Stand-alone shell (version 2.1)
> ls -l /bin/sh /bin/bash; dpkg -S /bin/sh; dpkg -l | grep bash
-rwxr-xr-x   1 root     root       447760 Aug 16 05:37 /bin/bash
lrwxrwxrwx   1 root     root            4 Aug 30 16:03 /bin/sh -> bash
bash: /bin/sh
ii  bash            2.02.1-1.6     The GNU Bourne Again SHell
> dpkg --force-depends --force-remove-essential --remove bash
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg - warning, overriding problem because --force enabled:
 This is an essential package - it should not be removed.
dpkg: bash: dependency problems, but removing anyway as you request:
 bug depends on bash (>= 2.01.1-1).
(Reading database ... 44579 files and directories currently installed.)
Removing bash ...
> ls -l /bin/sh /bin/bash; dpkg -S /bin/sh; dpkg -l | grep bash
> dpkg --unpack bash_2.02.1-1.7_i386.deb
Selecting previously deselected package bash.
(Reading database ... 44440 files and directories currently installed.)
Unpacking bash (from bash_2.02.1-1.7_i386.deb) ...
Adding symlink /bin/sh to /bin/bash.
> ls -l /bin/sh /bin/bash; dpkg -S /bin/sh; dpkg -l | grep bash
-rwxr-xr-x   1 root     root       447760 Aug 30 15:44 /bin/bash
lrwxrwxrwx   1 root     root            9 Aug 30 16:03 /bin/sh -> /bin/bash
dpkg: /bin/sh not found.
iU  bash            2.02.1-1.7     The GNU Bourne Again SHell
> dpkg --configure bash
Setting up bash (2.02.1-1.7) ...

> ls -l /bin/sh /bin/bash; dpkg -S /bin/sh; dpkg -l | grep bash
-rwxr-xr-x   1 root     root       447760 Aug 30 15:44 /bin/bash
lrwxrwxrwx   1 root     root            9 Aug 30 16:03 /bin/sh -> /bin/bash
dpkg: /bin/sh not found.
ii  bash            2.02.1-1.7     The GNU Bourne Again SHell
> 
Script done on Mon Aug 30 16:04:21 1999
====REMOVAL AND REINSTALLATION====

Next, when Joe Average decides to upgrade bash from one release to the
next:

====UPGRADE====
Script started on Mon Aug 30 16:04:30 1999
Stand-alone shell (version 2.1)
> ls -l /bin/sh /bin/bash; dpkg -S /bin/sh; dpkg -l | grep bash
-rwxr-xr-x   1 root     root       447760 Aug 16 05:37 /bin/bash
lrwxrwxrwx   1 root     root            4 Aug 30 16:04 /bin/sh -> bash
bash: /bin/sh
ii  bash            2.02.1-1.6     The GNU Bourne Again SHell
> dpkg --unpack bash_2.02.1-1.7_i386.deb
(Reading database ... 44579 files and directories currently installed.)
Preparing to replace bash 2.02.1-1.6 (using bash_2.02.1-1.7_i386.deb) ...
Adding symlink /bin/sh to /bin/bash.
Unpacking replacement bash ...
> ls -l /bin/sh /bin/bash; dpkg -S /bin/sh; dpkg -l | grep bash
-rwxr-xr-x   1 root     root       447760 Aug 30 15:44 /bin/bash
lrwxrwxrwx   1 root     root            9 Aug 30 16:04 /bin/sh -> /bin/bash
diversion by bash-preinst from: /bin/sh
diversion by bash-preinst to: /bin/sh.bash-old
iU  bash            2.02.1-1.7     The GNU Bourne Again SHell
> dpkg --configure bash
Setting up bash (2.02.1-1.7) ...

> ls -l /bin/sh /bin/bash; dpkg -S /bin/sh; dpkg -l | grep bash
-rwxr-xr-x   1 root     root       447760 Aug 30 15:44 /bin/bash
lrwxrwxrwx   1 root     root            9 Aug 30 16:04 /bin/sh -> /bin/bash
dpkg: /bin/sh not found.
ii  bash            2.02.1-1.7     The GNU Bourne Again SHell
> 
Script done on Mon Aug 30 16:05:23 1999
====UPGRADE====

Finally, when Jill Not-so-average, who's followed the aforementioned
advice about diverting /bin/sh tries the upgrade: (the main problem
being that she has to remove the diversion before the upgrade will go
ahead. It's the preinst that's failing however, so no actual damage
is done. It'd probably be a good idea to make the error message a
little bit more helpful in this case, actually)

====UPGRADE WITH DIVERSION====
Script started on Mon Aug 30 16:07:31 1999
Stand-alone shell (version 2.1)
> dpkg-divert --rename --add /bin/sh
Adding `local diversion of /bin/sh to /bin/sh.distrib'
> ln -s ash /bin/sh
> ls -l /bin/sh /bin/bash; dpkg -S /bin/sh; dpkg -l | grep bash
-rwxr-xr-x   1 root     root       447760 Aug 16 05:37 /bin/bash
lrwxrwxrwx   1 root     root            3 Aug 30 16:07 /bin/sh -> ash
local diversion from: /bin/sh
local diversion to: /bin/sh.distrib
bash: /bin/sh
ii  bash            2.02.1-1.6     The GNU Bourne Again SHell
> dpkg --install bash_2.02.1-1.6_i386.deb                
(Reading database ... 44580 files and directories currently installed.)
Preparing to replace bash 2.02.1-1.6 (using bash_2.02.1-1.6_i386.deb) ...
Unpacking replacement bash ...
Setting up bash (2.02.1-1.6) ...

> ls -l /bin/sh /bin/bash; dpkg -S /bin/sh; dpkg -l | grep bash
-rwxr-xr-x   1 root     root       447760 Aug 16 05:37 /bin/bash
lrwxrwxrwx   1 root     root            3 Aug 30 16:07 /bin/sh -> ash
local diversion from: /bin/sh
local diversion to: /bin/sh.distrib
bash: /bin/sh
ii  bash            2.02.1-1.6     The GNU Bourne Again SHell
> dpkg --unpack bash_2.02.1-1.7_i386.deb
(Reading database ... 44580 files and directories currently installed.)
Preparing to replace bash 2.02.1-1.6 (using bash_2.02.1-1.7_i386.deb) ...
dpkg-divert: `diversion of /bin/sh to /bin/sh.bash-old by bash-preinst' clashes with `local diversion of /bin/sh to /bin/sh.distrib'
/bin/sh: Couldn't ensure /bin/sh won't be lost during upgrade; aborting
dpkg: error processing bash_2.02.1-1.7_i386.deb (--unpack):
 subprocess pre-installation script returned error exit status 1
Errors were encountered while processing:
 bash_2.02.1-1.7_i386.deb
> dpkg-divert --remove /bin/sh
Removing `local diversion of /bin/sh to /bin/sh.distrib'
> dpkg --install bash_2.02.1-1.7_i386.deb
(Reading database ... 44579 files and directories currently installed.)
Preparing to replace bash 2.02.1-1.6 (using bash_2.02.1-1.7_i386.deb) ...
Adding symlink /bin/sh to /bin/bash.
Unpacking replacement bash ...
Setting up bash (2.02.1-1.7) ...

> ls -l /bin/sh /bin/bash; dpkg -S /bin/sh; dpkg -l | grep bash
-rwxr-xr-x   1 root     root       447760 Aug 30 15:44 /bin/bash
lrwxrwxrwx   1 root     root            9 Aug 30 16:09 /bin/sh -> /bin/bash
dpkg: /bin/sh not found.
ii  bash            2.02.1-1.7     The GNU Bourne Again SHell
> ln -sf ash /bin/sh
> dpkg --install bash_2.02.1-1.7_i386.deb
(Reading database ... 44578 files and directories currently installed.)
Preparing to replace bash 2.02.1-1.7 (using bash_2.02.1-1.7_i386.deb) ...
Unpacking replacement bash ...
Setting up bash (2.02.1-1.7) ...

> ls -l /bin/sh /bin/bash; dpkg -S /bin/sh; dpkg -l | grep bash
-rwxr-xr-x   1 root     root       447760 Aug 30 15:44 /bin/bash
lrwxrwxrwx   1 root     root            3 Aug 30 16:09 /bin/sh -> ash
dpkg: /bin/sh not found.
ii  bash            2.02.1-1.7     The GNU Bourne Again SHell
> 
Script done on Mon Aug 30 16:10:29 1999
====UPGRADE WITH DIVERSION====

Cheers,
aj

-- 
Anthony Towns <aj@humbug.org.au> <http://azure.humbug.org.au/~aj/>
I don't speak for anyone save myself. PGP encrypted mail preferred.

 ``The thing is: trying to be too generic is EVIL. It's stupid, it 
        results in slower code, and it results in more bugs.''
                                        -- Linus Torvalds


Reply to: