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
    

Reply via email to