Skip to content

Instantly share code, notes, and snippets.

@Zenexer
Last active May 15, 2020 01:23
Show Gist options
  • Save Zenexer/10bc12fa5c99848b4b2150184f6beee5 to your computer and use it in GitHub Desktop.
Save Zenexer/10bc12fa5c99848b4b2150184f6beee5 to your computer and use it in GitHub Desktop.

Upgrading to Ubuntu 17.10 on WSL

Note: This workaround is no longer necessary in Windows 10 Insider build 17046 and later. As of 2018-02-07, the patch hasn't yet made it to stable.

  1. Upgrade with do-release-upgrade, as you normally would. It will error out and leave your system in a partially upgraded state.
  2. Close all WSL terminals and ensure that all WSL processes have terminated
  3. Run as a normal user (not root): wget -O - https://gist.githubusercontent.com/Zenexer/10bc12fa5c99848b4b2150184f6beee5/raw/ubuntu-fix.sh | sh -s
  4. Answer any prompts (sudo password, version disambiguation)
  5. If there are errors, resolve them and re-run the script. It is idempotent, meaning that it is safe to run any number of times, even if it only partially completes.

Example errors that this resolves

Upgrading bash:

Preparing to unpack .../bash_4.4-5ubuntu1_amd64.deb ...
preinst: ../sysdeps/unix/sysv/linux/spawni.c:368: __spawnix: Assertion `ec >= 0' failed.
dpkg: error processing archive /var/cache/apt/archives/bash_4.4-5ubuntu1_amd64.deb (--unpack):

Upgrading packages that rely on invoke-rc.d for service control, such as ebtables and docker.io:

invoke-rc.d: could not determine current runlevel
 * Stopping Docker: docker
No process in pidfile '/var/run/docker-ssd.pid' found running; none killed.
invoke-rc.d: initscript docker, action "stop" failed.
dpkg: error processing package docker.io (--remove):
 subprocess installed pre-removal script returned error exit status 1
dpkg: error while cleaning up:
 subprocess installed post-installation script returned error exit status 1
Errors were encountered while processing:
 docker.io
E: Sub-process /usr/bin/dpkg returned an error code (1)
#!/bin/sh
set -e
if [ "$EUID" = '0' ]; then
echo "This script can't be run as root. Whenever root is needed, the script will use sudo." >&2
exit 1
fi
export DEBIAN_FRONTEND=noninteractive
echo "Running apt-get update" >&2
sudo apt-get update
# ebtables fix
# Credit to russalex: https://github.com/Microsoft/WSL/issues/143#issuecomment-209072634
# Credit to nuclearmistake: https://github.com/Microsoft/WSL/issues/143#issuecomment-209075558
echo "Implementing udev workaround (https://github.com/Microsoft/WSL/issues/143#issuecomment-209072634)" >&2
sudo tee /usr/sbin/policy-rc.d > /dev/null <<EOF
#!/bin/sh
exit 101
EOF
sudo chmod +x /usr/sbin/policy-rc.d
sudo dpkg-divert --local --rename --add /sbin/initctl
sudo ln -fs /bin/true /sbin/initctl
# Upgrade as much as we can
# Credit to heldchen: https://github.com/Microsoft/WSL/issues/1878#issuecomment-338401388
echo "Placing hold on bash (https://github.com/Microsoft/WSL/issues/1878#issuecomment-338401388)" >&2
sudo apt-mark hold bash
echo "Upgrade everything except bash" >&2
sudo apt-get dist-upgrade -y
# Fix bash preinst
echo "Creating temporary working directory" >&2
dir="`mktemp -d`"
pushd "$dir" > /dev/null
echo "Temp dirctory: $dir" >&2
package=bash
while echo "Downloading deb file for $package" >&2 && ! apt-get download "$package"; do
yn=
while [ "$yn" != 'y' ]; do
if ! read -rn1 -p "It looks like bash didn't download successfully. Do you want to try manually specifying a version? [y/n]: " yn; then
echo >&2
echo "Oops; there's no STDIN to read from, so assuming there's no interactive user to provide a version specifier" >&2
exit 1
fi
echo >&2
if [ "$yn" = 'n' ]; then
echo "Alright; fix whatever you need to fix, then rerun the script." >&2
exit 1
fi
done
version=
while [ -z "$version" ]; do
read -rp "Enter the version specifier (e.g., 4.4-5ubuntu1): " version
done
package="bash=$version"
done
echo "Locating downloaded deb file" >&2
deb="`echo bash_*.deb`"
if [ -z "$deb" -o "$deb" = 'bash_*.deb' ]; then
echo "Failed to find downloaded deb file for bash in $dir. Leaving temp directory for inspection. (Got: deb=$deb)" >&2
exit 1
fi
echo "Extracting $PWD/$deb to $PWD/deb" >&2
sudo dpkg-deb -x "$deb" deb
echo "Extracting $PWD/$deb metadata to $PWD/deb/DEBIAN" >&2
sudo dpkg-deb -e "$deb" deb/DEBIAN
echo "Replacing preinst binary with a WSL-friendly shell script" >&2
sudo tee deb/DEBIAN/preinst > /dev/null <<'EOD'
#!/bin/sh
set -e
backup() {
if exists "$1"; then
cp -dp "$1" "$2" || return $?
fi
return 0
}
exists() {
[ -e "$1" ] || return 1
}
force_symlink() {
if [ -e "$3" ] && ! unlink "$3"; then
echo "cannot create symlink $2 -> $1 (unlink)" >&2
exit 1
fi
if ! symlink "$1" "$3"; then
echo "cannot create symlink $2 -> $1 (symlink)" >&2
exit 1
fi
if ! rename "$3" "$2"; then
echo "cannot create symlink $2 -> $1 (rename)" >&2
exit 1
fi
return 0
}
symlink() {
ln -s "$1" "$2" || return $?
}
rename() {
mv -fT "$1" "$2" || return $?
}
reset_diversion() {
dpkg-divert --package bash --remove "$2" || return $?
dpkg-divert --package "$1" --divert "$3" --add "$2" || return $?
}
has_binsh_line() {
while read -r item; do
if [ "$item" = '/bin/sh' ]; then
return 0
fi
done
return 1
}
binsh_in_filelist() {
dpkg-query -L "$1" 2> /dev/null | has_binsh_line || return $?
}
undiverted() {
in="`dpkg-divert --listpackage "$1"`" || return $?
[ -z "$in" -o "$in" = 'bash' ] || return 1
}
access() {
# This isn't perfectly accurate, but it's sufficient for our purposes. The syscall is a little different.
[ -e "$1" ] || return 1
}
main() {
if access /bin/sh; then
backup /bin/sh /bin/sh.distrib || return $?
backup /usr/share/man/man1/sh.1.gz /usr/share/man/man1/sh.distrib.1.gz || return $?
force_symlink bash /bin/sh /bin/sh.temp || return $?
force_symlink bash.1.gz /usr/share/man/man1/sh.1.gz /usr/share/man/man1/sh.1.gz.temp || return $?
if ! binsh_in_filelist bash; then
return 0
fi
fi
if undiverted /bin/sh; then
reset_diversion dash /bin/sh /bin/sh.distrib || return $?
fi
if undiverted /usr/share/man/man1/sh.1.gz; then
reset_diversion dash /usr/share/man/man1/sh.1.gz || return $?
fi
return 0
}
main || exit $?
EOD
sudo chmod +x deb/DEBIAN/preinst
echo "Repackaging $PWD/$deb" >&2
sudo dpkg-deb -b deb "$deb"
echo "Releasing hold on bash" >&2
sudo apt-mark unhold bash
echo "Installing $PWD/$deb" >&2
sudo dpkg -i "$deb"
echo "Ensuring that everything worked" >&2
sudo apt-get dist-upgrade -y
echo "Cleaning up" >&2
popd > /dev/null
sudo rm -rf -- "$dir"
echo "You're good to go!" >&2
@arjvik
Copy link

arjvik commented May 30, 2018

Thank you so much for the Ebtables fix! How does this work though?

@MOZGIII
Copy link

MOZGIII commented Jun 10, 2018

Better ebtables fix: sudo rm -rf /var/lib/dpkg/info/ebtables.prerm
Then just sudo apt remove ebtables lxd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment