Просмотр исходного кода

maintscript-helper: new program to simplify some maintainer scripts

This program is designed to be run within maintainer scripts to achieve
some tasks that dpkg can't (yet) handle natively either because of design
decisions or due to current limitations.

Many of those tasks require coordinated actions from several maintainer
scripts (preinst, postinst, prerm, postrm). To avoid mistakes the same
call simply needs to be put in all scripts and the program will automatically
adapt its behaviour based on the environment variable DPKG_MAINTSCRIPT_NAME
and on the maintainer scripts arguments that you have to forward after
a double dash.
Raphaël Hertzog лет назад: 16
Родитель
Сommit
20f33fd89e
7 измененных файлов с 387 добавлено и 1 удалено
  1. 4 0
      debian/changelog
  2. 2 0
      debian/dpkg.install
  3. 1 0
      lib/Makefile.am
  4. 256 0
      lib/maintscript-helper
  5. 2 1
      man/Makefile.am
  6. 117 0
      man/maintscript-helper.1
  7. 5 0
      man/po/po4a.cfg

+ 4 - 0
debian/changelog

@@ -37,6 +37,10 @@ dpkg (1.15.6.2) UNRELEASED; urgency=low
   * dpkg now exports DPKG_LIBDIR to maintainer scripts pointing to the
   * dpkg now exports DPKG_LIBDIR to maintainer scripts pointing to the
     private directory containing internal programs like the upcoming
     private directory containing internal programs like the upcoming
     maintscript-helper.
     maintscript-helper.
+  * Add $DPKG_LIBDIR/maintscript-helper program that can be used in
+    maintainer scripts to perform common operations working around
+    current dpkg limitations: first version supports removing obsolete
+    conffiles and renaming conffiles. Closes: #514316
 
 
   [ Colin Watson ]
   [ Colin Watson ]
   * Modern tar files typically use NormalFile1 rather than NormalFile0 for
   * Modern tar files typically use NormalFile1 rather than NormalFile0 for

+ 2 - 0
debian/dpkg.install

@@ -11,6 +11,7 @@ usr/bin/dpkg-split
 usr/bin/dpkg-statoverride
 usr/bin/dpkg-statoverride
 usr/bin/dpkg-trigger
 usr/bin/dpkg-trigger
 usr/bin/update-alternatives
 usr/bin/update-alternatives
+usr/lib/dpkg/maintscript-helper
 usr/lib/dpkg/mksplit
 usr/lib/dpkg/mksplit
 usr/sbin
 usr/sbin
 usr/share/dpkg
 usr/share/dpkg
@@ -23,6 +24,7 @@ usr/share/man/{*/*,*}/dpkg-statoverride.8
 usr/share/man/{*/*,*}/dpkg-trigger.1
 usr/share/man/{*/*,*}/dpkg-trigger.1
 usr/share/man/{*/*,*}/dpkg.cfg.5
 usr/share/man/{*/*,*}/dpkg.cfg.5
 usr/share/man/{*/*,*}/dpkg.1
 usr/share/man/{*/*,*}/dpkg.1
+usr/share/man/{*/*,*}/maintscript-helper.1
 usr/share/man/{*/*,*}/start-stop-daemon.8
 usr/share/man/{*/*,*}/start-stop-daemon.8
 usr/share/man/{*/*,*}/update-alternatives.8
 usr/share/man/{*/*,*}/update-alternatives.8
 usr/share/perl5/Dpkg.pm
 usr/share/perl5/Dpkg.pm

+ 1 - 0
lib/Makefile.am

@@ -4,3 +4,4 @@ SUBDIRS = \
 	compat \
 	compat \
 	dpkg
 	dpkg
 
 
+pkglib_SCRIPTS = maintscript-helper

+ 256 - 0
lib/maintscript-helper

@@ -0,0 +1,256 @@
+#!/bin/sh
+#
+# Copyright © 2010 Raphaël Hertzog <hertzog@debian.org>
+# Copyright © 2008 Joey Hess <joeyh@debian.org>
+# Copyright © 2007 Guillem Jover (modifications on wiki.debian.org)
+# Copyright © 2005 Scott James Remnant (original implementation on www.dpkg.org)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# The conffile related functions are inspired by
+# http://wiki.debian.org/DpkgConffileHandling
+
+# This script is documented in maintscript-helper(1)
+
+##
+## Functions to remove an obsolete conffile during upgrade
+##
+rm_conffile() {
+	local CONFFILE="$1"
+	local LASTVERSION="$2"
+	local PACKAGE="$3"
+	if [ "$PACKAGE" = "--" -o -z "$PACKAGE" ]; then
+		PACKAGE="$DPKG_MAINTSCRIPT_PACKAGE"
+	fi
+	# Skip remaining parameters up to --
+	while [ "$1" != "--" -a "$1" != "" ]; do shift; done
+	shift
+
+	[ -n "$PACKAGE" ] || error "couldn't identify the package"
+	[ -n "$1" ] || error "maintainer script parameters are missing"
+	[ -n "$DPKG_MAINTSCRIPT_NAME" ] || \
+		error "environment variable DPKG_MAINTSCRIPT_NAME is required"
+
+	debug "Executing $0 rm_conffile in $DPKG_MAINTSCRIPT_NAME "\
+	      "of $DPKG_MAINTSCRIPT_PACKAGE"
+	debug "CONFFILE=$CONFFILE PACKAGE=$PACKAGE "\
+	      "LASTVERSION=$LASTVERSION ACTION=$1 PARAM=$2"
+	case "$DPKG_MAINTSCRIPT_NAME" in
+	preinst)
+		if [ "$1" = "install" -o "$1" = "upgrade" ] &&
+		   dpkg --compare-versions "$2" le-nl "$LASTVERSION"; then
+			prepare_rm_conffile "$CONFFILE" "$PACKAGE"
+		fi
+		;;
+	postinst)
+		if [ "$1" = "configure" ] &&
+		   dpkg --compare-versions "$2" le-nl "$LASTVERSION"; then
+			finish_rm_conffile $CONFFILE
+		fi
+		;;
+	postrm)
+		if [ "$1" = "purge" ]; then
+			rm -f "$CONFFILE.dpkg-bak" "$CONFFILE.dpkg-remove"
+		fi
+		if [ "$1" = "abort-install" -o "$1" = "abort-upgrade" ] &&
+		   dpkg --compare-versions "$2" le-nl "$LASTVERSION"; then
+			abort_rm_conffile "$CONFFILE"
+		fi
+		;;
+	*)
+		debug "$0 rm_conffile not required in $DPKG_MAINTSCRIPT_NAME"
+		;;
+	esac
+}
+
+prepare_rm_conffile() {
+	local CONFFILE="$1"
+	local PACKAGE="$2"
+
+	[ -e "$CONFFILE" ] || return 0
+
+	local md5sum="$(md5sum $CONFFILE | sed -e 's/ .*//')"
+	local old_md5sum="$(dpkg-query -W -f='${Conffiles}' $PACKAGE | \
+		sed -n -e "\' $CONFFILE ' { s/ obsolete$//; s/.* //; p }")"
+	if [ "$md5sum" != "$old_md5sum" ]; then
+		echo "Obsolete conffile $CONFFILE has been modified by you."
+		echo "Saving as $CONFFILE.dpkg-bak ..."
+		mv -f "$CONFFILE" "$CONFFILE.dpkg-bak"
+	else
+		echo "Moving obsolete conffile $CONFFILE out of the way..."
+		mv -f "$CONFFILE" "$CONFFILE.dpkg-remove"
+	fi
+}
+
+finish_rm_conffile() {
+	local CONFFILE="$1"
+
+	if [ -e "$CONFFILE.dpkg-remove" ]; then
+		echo "Removing obsolete conffile $CONFFILE ..."
+		rm -f "$CONFFILE.dpkg-remove"
+	fi
+}
+
+abort_rm_conffile() {
+	local CONFFILE="$1"
+
+	if [ -e "$CONFFILE.dpkg-remove" ]; then
+		echo "Reinstalling $CONFFILE that was moved away"
+		mv "$CONFFILE.dpkg-remove" "$CONFFILE"
+	fi
+	if [ -e "$CONFFILE.dpkg-bak" ]; then
+		echo "Reinstalling $CONFFILE that was backupped"
+		mv "$CONFFILE.dpkg-bak" "$CONFFILE"
+	fi
+}
+
+##
+## Functions to rename a conffile during upgrade
+##
+mv_conffile() {
+	local OLDCONFFILE="$1"
+	local NEWCONFFILE="$2"
+	local LASTVERSION="$3"
+	local PACKAGE="$4"
+	if [ "$PACKAGE" = "--" -o -z "$PACKAGE" ]; then
+		PACKAGE="$DPKG_MAINTSCRIPT_PACKAGE"
+	fi
+	# Skip remaining parameters up to --
+	while [ "$1" != "--" -a "$1" != "" ]; do shift; done
+	shift
+
+	[ -n "$PACKAGE" ] || error "couldn't identify the package"
+	[ -n "$1" ] || error "maintainer script parameters are missing"
+	[ -n "$DPKG_MAINTSCRIPT_NAME" ] || \
+		error "environment variable DPKG_MAINTSCRIPT_NAME is required"
+
+	debug "Executing $0 mv_conffile in $DPKG_MAINTSCRIPT_NAME "\
+	      "of $DPKG_MAINTSCRIPT_PACKAGE"
+	debug "CONFFILE=$OLDCONFFILE -> $NEWCONFFILE PACKAGE=$PACKAGE "\
+	      "LASTVERSION=$LASTVERSION ACTION=$1 PARAM=$2"
+	case "$DPKG_MAINTSCRIPT_NAME" in
+	preinst)
+		if [ "$1" = "install" -o "$1" = "upgrade" ] &&
+		   dpkg --compare-versions "$2" le-nl "$LASTVERSION"; then
+			prepare_mv_conffile "$OLDCONFFILE" "$PACKAGE"
+		fi
+		;;
+	postinst)
+		if [ "$1" = "configure" ] &&
+		   dpkg --compare-versions "$2" le-nl "$LASTVERSION"; then
+			finish_mv_conffile "$OLDCONFFILE" "$NEWCONFFILE"
+		fi
+		;;
+	postrm)
+		if [ "$1" = "abort-install" -o "$1" = "abort-upgrade" ] &&
+		   dpkg --compare-versions "$2" le-nl "$LASTVERSION"; then
+			abort_rm_conffile "$OLDCONFFILE"
+		fi
+		;;
+	*)
+		debug "$0 mv_conffile not required in $DPKG_MAINTSCRIPT_NAME"
+		;;
+	esac
+}
+
+prepare_mv_conffile() {
+	local CONFFILE="$1"
+	local PACKAGE="$2"
+
+	[ -e "$CONFFILE" ] || return 0
+
+	local md5sum="$(md5sum $CONFFILE | sed -e 's/ .*//')"
+	local old_md5sum="$(dpkg-query -W -f='${Conffiles}' $PACKAGE | \
+		sed -n -e "\' $CONFFILE ' { s/ obsolete$//; s/.* //; p }")"
+	if [ "$md5sum" = "$old_md5sum" ]; then
+		mv -f "$CONFFILE" "$CONFFILE.dpkg-remove"
+	fi
+}
+
+finish_mv_conffile() {
+	local OLDCONFFILE="$1"
+	local NEWCONFFILE="$2"
+
+	rm -f $OLDCONFFILE.dpkg-remove
+
+	[ -e "$OLDCONFFILE" ] || return 0
+
+	echo "Preserving user changes to $NEWCONFFILE (renamed from $OLDCONFFILE)..."
+	mv -f "$NEWCONFFILE" "$NEWCONFFILE.dpkg-new"
+	mv -f "$OLDCONFFILE" "$NEWCONFFILE"
+}
+
+abort_mv_conffile() {
+	local CONFFILE="$1"
+
+	if [ -e "$CONFFILE.dpkg-remove" ]; then
+		echo "Reinstalling $CONFFILE that was moved away"
+		mv "$CONFFILE.dpkg-remove" "$CONFFILE"
+	fi
+}
+
+# Common functions
+debug() {
+	if [ -n "$DPKG_DEBUG" ]; then
+		echo "DEBUG: $PROGNAME: $1" >&2
+	fi
+}
+
+error() {
+	echo "ERROR: $PROGNAME: $1" >&2
+	exit 1
+}
+
+usage() {
+	cat <<END
+Syntax: $0 <command> [<parameters>] -- <maintainer script parameters>
+
+Commands and parameters:
+
+  rm_conffile <conffile> <last-version> [<package>]
+	Remove obsolete conffile.
+	Must be called in preinst, postinst and postrm.
+
+  mv_conffile <old-conf> <new-conf> <last-version> [<package>]
+	Rename a conffile.
+	Must be called in preinst, postinst and postrm.
+
+  help
+	Display this usage information.
+END
+}
+
+# Main code
+set -e
+
+PROGNAME=$(basename $0)
+command="$1"
+shift
+
+case "$command" in
+rm_conffile)
+	rm_conffile "$@"
+	;;
+mv_conffile)
+	mv_conffile "$@"
+	;;
+--help|help|-?|-h)
+	usage
+	;;
+*)
+	usage
+	exit 1
+esac
+
+exit 0

+ 2 - 1
man/Makefile.am

@@ -118,7 +118,8 @@ dist_man_MANS = \
 	dpkg-trigger.1 \
 	dpkg-trigger.1 \
 	dpkg-vendor.1 \
 	dpkg-vendor.1 \
 	dpkg.1 \
 	dpkg.1 \
-	dpkg.cfg.5
+	dpkg.cfg.5 \
+	maintscript-helper.1
 
 
 if WITH_DSELECT
 if WITH_DSELECT
 dist_man_MANS += \
 dist_man_MANS += \

+ 117 - 0
man/maintscript-helper.1

@@ -0,0 +1,117 @@
+.TH maintscript\-helper 1 "2010-04-16" "Debian Project" "dpkg suite"
+.SH NAME
+maintscript\-helper \- works around known dpkg limitations in maintainer scripts
+.
+.SH SYNOPSIS
+.B $DPKG_LIBDIR/maintscript\-helper
+.IR command " [" parameters "...] \-\- " maint-script-parameters
+.
+.SH COMMANDS AND PARAMETERS
+.P
+\fBrm_conffile\fP \fIconffile\fP \fIlastversion\fP [\fIpackage\fP]
+.P
+\fBmv_conffile\fP \fIoldconffile\fP \fInewconffile\fP \fIlastversion\fP [\fIpackage\fP]
+.
+.SH DESCRIPTION
+.P
+This program is designed to be run within maintainer scripts to achieve
+some tasks that dpkg can't (yet) handle natively either because of design
+decisions or due to current limitations.
+.P
+Many of those tasks require coordinated actions from several maintainer
+scripts (\fBpreinst\fP, \fBpostinst\fP, \fBprerm\fP, \fBpostrm\fP). To
+avoid mistakes the same call simply needs to be put in all scripts and the
+program will automatically adapt its behaviour based on the environment
+variable DPKG_MAINTSCRIPT_NAME and on the maintainer scripts arguments
+that you have to forward after a double dash.
+.
+.SH CONFFILE RELATED TASKS
+.P
+When upgrading a package, dpkg will not automatically remove a conffile (a
+configuration file for which dpkg should preserve user changes) if it is
+not present in the newer version. There are two principal reasons for
+this; the first is that the conffile could've been dropped by accident and
+the next version could restore it, users wouldn't want their changes
+thrown away. The second is to allow packages to transition files from a
+dpkg\-maintained conffile to a file maintained by the package's maintainer
+scripts, usually with a tool like debconf or ucf.
+.P
+This means that if a package is intended to rename or remove a conffile,
+it must explicitly do so and \fBmaintscript\-helper\fP can be used to implement
+graceful deletion and moving of conffiles within maintainer scripts.
+.
+.SS REMOVING A CONFFILE
+.P
+If a conffile is completely removed, it should be removed from disk,
+unless the user has modified it. If there are local modifications, they
+should be preserved. If the package upgrades aborts, the newly obsolete
+conffile should not disappear.
+.P
+All of this is implemented by putting the following shell snippet in the
+\fBpreinst\fP, \fBpostinst\fP and \fBpostrm\fP maintainer scripts:
+.P
+    if [ \-x $DPKG_LIBDIR/maintscript\-helper ]; then
+        $DPKG_LIBDIR/maintscript\-helper rm_conffile \\
+            \fIconffile\fP \fIlastversion\fP \fIpackage\fP \-\- "$@"
+    fi
+.P
+\fIconffile\fP is the filename of the conffile to remove.
+\fIlastversion\fP is the last version of the package that contained the
+conffile. \fIpackage\fP is the package name, it's optional as it will
+default to $DPKG_MAINTSCRIPT_PACKAGE (this variable is set by dpkg to the
+name of the package acted upon). All the parameters of the maintainer
+scripts have to be forwarded to the program after "\-\-".
+.P
+Current implementation: in the \fBpreinst\fP, it checks if the conffile
+was modified and renames it either to \fIconffile\fP\fB.dpkg\-remove\fP (if not
+modified) or to \fIconffile\fP\fB.dpkg\-bak\fP (if modified). The latter file is
+kept for reference as it contains user modifications but the former will
+be removed in the \fBpostinst\fP. If the package upgrade aborts, the
+\fBpostrm\fP reinstalls the original conffile. During purge, the
+\fBpostrm\fP will also delete the \fB.dpkg\-bak\fP file kept up to now.
+.
+.SS RENAMING A CONFFILE
+.P
+If a conffile is moved from one location to another, you need to make sure
+you move across any changes the user has made. This may seem a simple
+change to the \fBpreinst\fP script at first, however that will result in
+the user being prompted by dpkg to approve the conffile edits even though
+they are not responsible of them.
+.P
+Graceful renaming can be implemented by putting the following shell
+snippet in the \fBpreinst\fP, \fBpostinst\fP and \fBpostrm\fP maintainer
+scripts:
+.P
+    if [ \-x $DPKG_LIBDIR/maintscript\-helper ]; then
+        $DPKG_LIBDIR/maintscript\-helper mv_conffile \\
+            \fIoldconffile\fP \fInewconffile\fP \fIlastversion\fP \fIpackage\fP \-\- "$@"
+    fi
+.P
+\fIoldconffile\fP and \fInewconffile\fP are the old and new name of the
+conffile to rename. \fIlastversion\fP is the last version of the package
+that contained the conffile with the old name. \fIpackage\fP is the
+package name, it's optional as it will default to
+$DPKG_MAINTSCRIPT_PACKAGE (this variable is set by dpkg to the name of the
+package acted upon). All the parameters of the maintainer scripts have to
+be forwarded to the program after "\-\-".
+.P
+Current implementation: the \fBpreinst\fP checks if the conffile has been
+modified, if yes it's left on place otherwise it's renamed to
+\fIoldconffile\fP\fB.dpkg\-remove\fP. On configuration, the \fBpostinst\fP
+removes \fIoldconffile\fP\fB.dpkg\-remove\fP and renames \fIoldconffile\fP
+to \fInewconffile\fP if \fIoldconffile\fP is still available. On
+abort\-ugrade/abort\-install, the \fBpostrm\fP renames
+\fIoldconffile\fP\fB.dpkg\-remove\fP back to \fIoldconffile\fP if required.
+.
+.SH AUTHORS
+Copyright \(co 2010 Rapha\[:e]l Hertzog
+.br
+Copyright \(co 2008 Joey Hess
+.br
+Copyright \(co 2007 Guillem Jover
+.br
+Copyright \(co 2005 Scott James Remnant
+.sp
+This is free software; see the GNU General Public Licence version 2 or
+later for copying conditions. There is NO WARRANTY.
+

+ 5 - 0
man/po/po4a.cfg

@@ -193,6 +193,11 @@
            add_$lang:po/$lang.add
            add_$lang:po/$lang.add
 
 
 
 
+[type:man] maintscript-helper.1       \
+           $lang:$(builddir)/$lang/maintscript-helper.1     \
+           add_$lang:po/$lang.add
+
+
 [type:man] start-stop-daemon.8        \
 [type:man] start-stop-daemon.8        \
            $lang:$(builddir)/$lang/start-stop-daemon.8      \
            $lang:$(builddir)/$lang/start-stop-daemon.8      \
            add_$lang:po/$lang.add
            add_$lang:po/$lang.add