diff options
-rw-r--r-- | debian/changelog | 4 | ||||
-rw-r--r-- | debian/dpkg.install | 2 | ||||
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rwxr-xr-x | lib/maintscript-helper | 256 | ||||
-rw-r--r-- | man/Makefile.am | 3 | ||||
-rw-r--r-- | man/maintscript-helper.1 | 117 | ||||
-rw-r--r-- | man/po/po4a.cfg | 5 |
7 files changed, 387 insertions, 1 deletions
diff --git a/debian/changelog b/debian/changelog index 06757d768..30440e62f 100644 --- a/debian/changelog +++ b/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 private directory containing internal programs like the upcoming 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 ] * Modern tar files typically use NormalFile1 rather than NormalFile0 for diff --git a/debian/dpkg.install b/debian/dpkg.install index 30bf847f0..fd86914b3 100644 --- a/debian/dpkg.install +++ b/debian/dpkg.install @@ -11,6 +11,7 @@ usr/bin/dpkg-split usr/bin/dpkg-statoverride usr/bin/dpkg-trigger usr/bin/update-alternatives +usr/lib/dpkg/maintscript-helper usr/lib/dpkg/mksplit usr/sbin usr/share/dpkg @@ -23,6 +24,7 @@ usr/share/man/{*/*,*}/dpkg-statoverride.8 usr/share/man/{*/*,*}/dpkg-trigger.1 usr/share/man/{*/*,*}/dpkg.cfg.5 usr/share/man/{*/*,*}/dpkg.1 +usr/share/man/{*/*,*}/maintscript-helper.1 usr/share/man/{*/*,*}/start-stop-daemon.8 usr/share/man/{*/*,*}/update-alternatives.8 usr/share/perl5/Dpkg.pm diff --git a/lib/Makefile.am b/lib/Makefile.am index 40c2a6b5b..deb9b9bbf 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -4,3 +4,4 @@ SUBDIRS = \ compat \ dpkg +pkglib_SCRIPTS = maintscript-helper diff --git a/lib/maintscript-helper b/lib/maintscript-helper new file mode 100755 index 000000000..d8f81bbd9 --- /dev/null +++ b/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 diff --git a/man/Makefile.am b/man/Makefile.am index 834883254..1dcb2dd95 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -118,7 +118,8 @@ dist_man_MANS = \ dpkg-trigger.1 \ dpkg-vendor.1 \ dpkg.1 \ - dpkg.cfg.5 + dpkg.cfg.5 \ + maintscript-helper.1 if WITH_DSELECT dist_man_MANS += \ diff --git a/man/maintscript-helper.1 b/man/maintscript-helper.1 new file mode 100644 index 000000000..f1ead944d --- /dev/null +++ b/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. + diff --git a/man/po/po4a.cfg b/man/po/po4a.cfg index 80089ae40..92e52a178 100644 --- a/man/po/po4a.cfg +++ b/man/po/po4a.cfg @@ -193,6 +193,11 @@ 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 \ $lang:$(builddir)/$lang/start-stop-daemon.8 \ add_$lang:po/$lang.add |