On Mon, Jan 30, 2017 at 06:38:16PM +0000, Robie Basak wrote: > > So how about this, just a sketch at the moment rather than a full > > patch? > > Your sketch seems good to me, assuming that "dpkg-query --search" is > permitted from maintainer scripts (I know there are some re-entrancy > problems with some particular types of dpkg-related invocations?) > [...] > > I wondered this too. But without thinking it through in more detail, > it'd certainly be better than what we have now.
Please find attached a patch against the current mysql-5.7/debian/master branch on alioth. I have also updated the github branch for mariadb-10.1 to address the same issue (the one with a pull request to otto/mariadb-10.1). It's been tested for upgrades, clean install, remove and purge. Best wishes, Julian
diff --git a/debian/mysql-server-5.7.lintian-overrides b/debian/mysql-server-5.7.lintian-overrides index f1672c49..c5732994 100644 --- a/debian/mysql-server-5.7.lintian-overrides +++ b/debian/mysql-server-5.7.lintian-overrides @@ -1,3 +1,5 @@ # These long lines reproduce actual output and to reformat them # would damage the integrity of the man page. manpage-has-errors-from-man usr/share/man/man1/mysqlbinlog.1.gz 1893: warning [p 13, 5.3i, div `3tbd3,2', 0.8i]: can't break line +# the second update-rc.d is informational only: see postrm for details +mysql-server-5.7: duplicate-updaterc.d-calls-in-postrm mysql diff --git a/debian/mysql-server-5.7.postinst b/debian/mysql-server-5.7.postinst index 54adbd09..e7e7f823 100755 --- a/debian/mysql-server-5.7.postinst +++ b/debian/mysql-server-5.7.postinst @@ -22,27 +22,8 @@ run_init_sql() { return $result } -# To avoid having hardcoded paths in the script, we do a search on the path, as suggested at: -# https://www.debian.org/doc/manuals/developers-reference/ch06.en.html#bpp-debian-maint-scripts -pathfind() { - OLDIFS="$IFS" - IFS=: - for p in $PATH; do - if [ -x "$p/$*" ]; then - IFS="$OLDIFS" - return 0 - fi - done - IFS="$OLDIFS" - return 1 -} - invoke() { - if pathfind invoke-rc.d; then - invoke-rc.d mysql $1 - else - /etc/init.d/mysql $1 - fi + invoke-rc.d mysql $1 } # Check if server is able to start. If it fails we abort early and refer @@ -315,6 +296,27 @@ if [ "$1" = "configure" ]; then invoke start fi fi + + # Fix broken postrms in mysql-server-5.[1-6] and mariadb-server-10.[01] + # packages to prevent purging these packages from breaking our package. + # (See #852495) It is unlikely that a user would have mariadb-server + # postrm files lying around, but we aim to be safe. + + # We comment out all of the commands which assume that there is no other + # mysql server installed. + # (Because of the Conflicts in the control file for this package, they can + # only possibly be in a configuration-only state at this point. And this + # cannot harm even if the system is in a very broken state and we are being + # configured in spite of those packages being in a different state.) + olds=$(ls /var/lib/dpkg/info/mysql-server-5.[1-6].postrm /var/lib/dpkg/info/mariadb-server-10.0.postrm 2>/dev/null || true) + if [ -f /var/lib/dpkg/info/mariadb-server-10.1.postrm ]; then + if ! grep -q fullpurge /var/lib/dpkg/info/mariadb-server-10.1.postrm; then + olds="$olds /var/lib/dpkg/info/mariadb-server-10.1.postrm" + fi + fi + if [ -n "$olds" ]; then + perl -i -pe 's/stop_server(?=\s|$)/# stop_server/; s/^(\s*)((?:update|invoke)-rc\.d.*$)/$1# $2\n$1true/; s/^(\s*)(deb-systemd-helper.*$)/$1# $2\n$1true/; s%rm -f "/etc/apparmor.d%# rm -f "/etc/apparmor.d%' $olds + fi fi # forget we ever saw the password. don't use reset to keep the seen status diff --git a/debian/mysql-server-5.7.postrm b/debian/mysql-server-5.7.postrm index 49d45f1b..a106ec29 100755 --- a/debian/mysql-server-5.7.postrm +++ b/debian/mysql-server-5.7.postrm @@ -11,49 +11,33 @@ if [ -n "$DEBIAN_SCRIPT_DEBUG" ]; then set -v -x; DEBIAN_SCRIPT_TRACE=1; fi ${DEBIAN_SCRIPT_TRACE:+ echo "#42#DEBUG# RUNNING $0 $*" 1>&2 } mysql_cfgdir=/etc/mysql -MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf" - -# To avoid having hardcoded paths in the script, we do a search on the path, as suggested at: -# https://www.debian.org/doc/manuals/developers-reference/ch06.en.html#bpp-debian-maint-scripts -pathfind() { - OLDIFS="$IFS" - IFS=: - for p in $PATH; do - if [ -x "$p/$*" ]; then - IFS="$OLDIFS" - return 0 - fi - done - IFS="$OLDIFS" - return 1 -} - -# Try to stop the server in a sane way. If it does not success let the admin -# do it himself. No database directories should be removed while the server -# is running! -stop_server() { - set +e - if pathfind invoke-rc.d; then - invoke-rc.d mysql stop - else - /etc/init.d/mysql stop - fi - errno=$? - set -e - if [ "$?" != 0 ]; then - echo "Trying to stop the MySQL server resulted in exitcode $?." 1>&2 - echo "Stop it yourself and try again!" 1>&2 - exit 1 +# Do we (offer to) do a full purge, or is there another mysql server +# present? This includes within it a check for "$1" = purge. +fullpurge=no +if [ "$1" = purge ]; then + if [ ! \( -x /usr/sbin/mysqld -o -L /usr/sbin/mysqld \) ]; then + fullpurge=yes + elif [ -x /usr/sbin/mysqld ]; then + # Does this /usr/sbin/mysqld belong to our server-core package? + # If so, we can offer to do a full purge, otherwise we do not. + + # Use the DPKG_MAINTSCRIPT_PACKAGE to avoid hardcoding the package + # name; this reduces the chances of future errors creeping in. + # If we're being run outside of a dpkg run, then this is safe. + package=${DPKG_MAINTSCRIPT_PACKAGE} + corepackage=$(echo "$package" | sed -e 's/server-/server-core-/') + daemonpackage=$(dpkg-query -S /usr/sbin/mysqld 2>/dev/null | cut -d: -f1) + if [ "$corepackage" = "$daemonpackage" -a -n "$corepackage" ]; then + fullpurge=yes + fi fi -} +fi case "$1" in purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) - if [ -n "`$MYADMIN ping 2>/dev/null`" ]; then - stop_server - sleep 2 - fi + # We handle purge actions below. The server has already been stopped + # if necessary by the prerm. ;; *) echo "postrm called with unknown argument '$1'" 1>&2 @@ -72,7 +56,7 @@ esac # # - When purging, ask if data files should also be removed # -if [ "$1" = "purge" ]; then +if [ $fullpurge = yes ]; then # we remove the mysql user only after all his owned files are purged rm -f /var/log/mysql.{log,err}{,.0,.[1234567].gz} rm -rf /var/log/mysql @@ -84,13 +68,84 @@ if [ "$1" = "purge" ]; then # never remove the debian.cnf when the databases are still existing # else we ran into big trouble on the next install! rm -f /etc/mysql/debian.cnf - rm -rf /var/lib/mysql + # Remove all contents from /var/lib/mysql except if it's a + # directory with file system data. See #829491 for details and + # #608938 for potential mysql-server leftovers which erroneously + # had been renamed. + # Attempt removal only if the directory hasn't already been removed + # by dpkg to avoid failing on "No such file or directory" errors. + if [ -d /var/lib/mysql ] + then + find /var/lib/mysql -mindepth 1 \ + -not -path '*/lost+found/*' -not -name 'lost+found' \ + -not -path '*/lost@002bfound/*' -not -name 'lost@002bfound' \ + -delete + + # "|| true" still needed as rmdir still exits with non-zero if + # /var/lib/mysql is a mount point + rmdir --ignore-fail-on-non-empty /var/lib/mysql || true + fi + rm -rf /var/run/mysqld # this directory is created by the init script, don't leave behind rm -rf /var/lib/mysql-files rm -rf /var/lib/mysql-keyring userdel mysql || true fi fi -#DEBHELPER# +# We manually add the debhelper snippets here to ensure that when purging, +# only safe-to-run snippets are run if another mysql-server or mariadb-server +# package is installed. + +# Automatically added by dh_installinit +# modified to protect against another mysql server package being installed +if [ $fullpurge = yes ]; then + update-rc.d mysql remove >/dev/null +fi + + +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [ -d /run/systemd/system ] ; then + systemctl --system daemon-reload >/dev/null || true +fi +# End automatically added section +# Automatically added by dh_systemd_enable +if [ "$1" = "remove" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper mask mysql.service >/dev/null + fi +fi + +# modified to protect against another mysql server package being installed +if [ $fullpurge = yes ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper purge mysql.service >/dev/null + deb-systemd-helper unmask mysql.service >/dev/null + fi +fi +# End automatically added section +# Automatically added by dh_installdebconf +if [ "$1" = purge ] && [ -e /usr/share/debconf/confmodule ]; then + . /usr/share/debconf/confmodule + db_purge +fi +# End automatically added section +# Automatically added by dh_apparmor +# modified to protect against another mysql server package being installed +if [ $fullpurge = yes ] && ! [ -e "/etc/apparmor.d/usr.sbin.mysqld" ] ; then + rm -f "/etc/apparmor.d/disable/usr.sbin.mysqld" || true + rm -f "/etc/apparmor.d/force-complain/usr.sbin.mysqld" || true + rm -f "/etc/apparmor.d/local/usr.sbin.mysqld" || true + rmdir /etc/apparmor.d/disable 2>/dev/null || true + rmdir /etc/apparmor.d/local 2>/dev/null || true + rmdir /etc/apparmor.d 2>/dev/null || true +fi +# End automatically added section exit 0 + +# We add the debhelper snippets here, after the exit 0, so that they can +# be visually checked in the future to ensure that nothing has been left +# out above. + +#DEBHELPER# diff --git a/debian/mysql-server-5.7.preinst b/debian/mysql-server-5.7.preinst index 4ac328cc..143cf61d 100755 --- a/debian/mysql-server-5.7.preinst +++ b/debian/mysql-server-5.7.preinst @@ -20,21 +20,6 @@ DATADIR=/var/lib/mysql LOGDIR=/var/log/mysql UPGRADEDIR=/var/lib/mysql-upgrade -# To avoid having hardcoded paths in the script, we do a search on the path, as suggested at: -# https://www.debian.org/doc/manuals/developers-reference/ch06.en.html#bpp-debian-maint-scripts -pathfind() { - OLDIFS="$IFS" - IFS=: - for p in $PATH; do - if [ -x "$p/$*" ]; then - IFS="$OLDIFS" - return 0 - fi - done - IFS="$OLDIFS" - return 1 -} - # Try to stop the server in a sane way. If it does not success let the admin # do it himself. No database directories should be removed while the server # is running! Another mysqld in e.g. a different chroot is fine for us. @@ -42,12 +27,7 @@ stop_server() { if [ ! -x /etc/init.d/mysql ]; then return; fi set +e - if pathfind invoke-rc.d; then - cmd="invoke-rc.d mysql stop" - else - cmd="/etc/init.d/mysql stop" - fi - $cmd + invoke-rc.d mysql stop errno=$? set -e