[PATCH] initial implementation of debconf frontend for conffile conflicts
when available, dpkg will now spawn a debconf-based frontend (which can be
found in /usr/share/dpkg/dpkg-promptconf.sh) for resolving conffile conflict
see TODO.debconf for a list of open questions and/or possible issues.
---
TODO.debconf | 70 +++++++++++++++++++++++++++++++
debian/control | 2 +-
debian/dpkg.install | 1 +
debian/dpkg.templates | 13 ++++++
debian/po/POTFILES.in | 1 +
debian/po/templates.pot | 59 ++++++++++++++++++++++++++
debian/rules | 2 +
lib/dpkg.h | 2 +
scripts/dpkg-confprompt.sh | 98 ++++++++++++++++++++++++++++++++++++++++++++
src/configure.c | 75 +++++++++++++++++++++++----------
10 files changed, 299 insertions(+), 24 deletions(-)
create mode 100644 TODO.debconf
create mode 100644 debian/dpkg.templates
create mode 100644 debian/po/POTFILES.in
create mode 100644 debian/po/templates.pot
create mode 100755 scripts/dpkg-confprompt.sh
diff --git a/TODO.debconf b/TODO.debconf
new file mode 100644
index 0000000..6db3177
--- /dev/null
+++ b/TODO.debconf
@@ -0,0 +1,70 @@
+- could my debconf "detection" be done better?
+
+ currently i just call debconf via system(), and i catch the particular
+ failure case where debconf fails to execute (return status = 127)
+
+- should i be using m_malloc() or varbuf functions for my string mangling?
+
+ more of a general question. currently i'm doing m_malloc() for stuff like
+ assembling a cmdline to pass to system, etc. but i wonder if i should be
+ using the varbuf related funcitons instead.
+
+- figure out how the templates should be managed within the source package
+
+ currently it's installed via the standard dh_installdebconf, but this is
+ a little awkward in the situation of dpkg, since dpkg itself does not use
+ the templates in the maintainer scripts and thus extra actions need to be
+ taken to register the templates files. perhaps the file shouldn't be
+ traditionally installed at all, and instead kept somewhere outside of
+ <admindir>/info, since we rely on it existing somewhere and it doesn't
+ feel right poking around in there.
+
+ also, this means that there is a second po directory in debian, which
+ makes life a little more difficult for translators, and removes the
+ ability to share a common set of strings between the debconf stuff
+ and the rest of dpkg.
+
+- debconf script implementation
+
+ currently this is done with a small shell script. while it uses mostly
+ built-in functions, it does mean at least 3 extra exec() calls per conffile
+ conflict (sh -c from system(), debconf, and then the script itself, plus
+ any non-builtin functions that these call). this could be made slightly
+ better in a few ways i can think of:
+
+ - use an exec() family function instead of system, saving one sh -c exec()
+ - enhance the program to persist over the invocation of dpkg, talking
+ over a socket or similar.
+
+ the script could also be re-implemented in C, and it's relatively simple
+ since it's basically printf/fgets calls wrapped in some logic, but i don't
+ see it being particularly faster--the only advantage i see is that it would
+ allow to share a bit of code (it would still need to be executed seperately,
+ as far as i can tell, due to needing to interface with debconf as an external
+ program
+
+- setting and respecting defaults in debconf frontend
+
+ currently the debconf frontend always defaults to "keep installed version",
+ but in some cases the default has been changed and needs to be communicated
+ into the frontend wrapper.
+
+- per-package/file registration of templates?
+
+ currently we use a single template that is subtituted over and over again
+ with the various questions. however, it's worth consideration if we want
+ to register the same template multiple times to different locations
+ (i.e. register dpkg/confprompt dpkg/confprompt/foo/etc/foo.cfg), so that
+ the answers to questions can persist like other debconf questions.
+
+- what should happen when someone selects "cancel"?
+
+ currently the window goes away, and pops up again :)
+
+- depends/recommends on debconf?
+
+ do we want to force/recommend having debconf available? also, the frontend
+ script uses the X_LOADTEMPLATEFILE interface, which is only available
+ after debconf 1.5.19 and cdebconf 0.106. however, if anything goes wrong
+ with the new frontend (lack of debconf, or internal failure of the
+ frontend), dpkg falls back to the "traditional" prompt.
diff --git a/debian/control b/debian/control
index 8393bf4..7bb3ef3 100644
--- a/debian/control
+++ b/debian/control
@@ -13,7 +13,7 @@ Standards-Version: 3.7.3
Build-Depends: debhelper (>= 4.1.81), pkg-config, po4a (>= 0.23),
libncursesw5-dev, zlib1g-dev (>= 1:1.1.3-19.1), libbz2-dev,
libselinux1-dev (>= 1.28-4) [!hurd-i386 !kfreebsd-i386 !kfreebsd-amd64],
- libtimedate-perl, libio-string-perl
+ libtimedate-perl, libio-string-perl, po-debconf
Package: dpkg
Architecture: any
diff --git a/debian/dpkg.install b/debian/dpkg.install
index 2f54618..bcace45 100644
--- a/debian/dpkg.install
+++ b/debian/dpkg.install
@@ -1,5 +1,6 @@
../dpkg.cfg etc/dpkg
../archtable usr/share/dpkg
+../../scripts/dpkg-confprompt.sh usr/share/dpkg
etc/alternatives
etc/dpkg/origins
diff --git a/debian/dpkg.templates b/debian/dpkg.templates
new file mode 100644
index 0000000..8778993
--- /dev/null
+++ b/debian/dpkg.templates
@@ -0,0 +1,13 @@
+Template: dpkg/promptconfaction
+Type: select
+_Choices: Install the package maintainer's version, Keep the currently installed version, Show the differences between the current and new versions, Invoke a shell to investigate
+Default: Keep the currently installed version
+_Description: Select how dpkg should resolve the conflicts for ${file}
+ Conflicts have been detected while updating a conffile in the package ${pkg}.
+ .
+ File: ${file}
+ .
+ Reason: ${reason}
+ .
+ Select from the provided options which action should be taken to resolve
+ the conflicts.
diff --git a/debian/po/POTFILES.in b/debian/po/POTFILES.in
new file mode 100644
index 0000000..b775c53
--- /dev/null
+++ b/debian/po/POTFILES.in
@@ -0,0 +1 @@
+[type: gettext/rfc822deb] dpkg.templates
diff --git a/debian/po/templates.pot b/debian/po/templates.pot
new file mode 100644
index 0000000..b45a96d
--- /dev/null
+++ b/debian/po/templates.pot
@@ -0,0 +1,59 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: dpkg@packages.debian.org\n"
+"POT-Creation-Date: 2008-03-15 11:59+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: select
+#. Choices
+#: ../dpkg.templates:1001
+msgid ""
+"Install the package maintainer's version, Keep the currently installed "
+"version, Show the differences between the current and new versions, Invoke a "
+"shell to investigate"
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../dpkg.templates:1002
+msgid "Select how dpkg should resolve the conflicts for ${file}"
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../dpkg.templates:1002
+msgid ""
+"Conflicts have been detected while updating a conffile in the package ${pkg}."
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../dpkg.templates:1002
+msgid "File: ${file}"
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../dpkg.templates:1002
+msgid "Reason: ${reason}"
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../dpkg.templates:1002
+msgid ""
+"Select from the provided options which action should be taken to resolve the "
+"conflicts."
+msgstr ""
diff --git a/debian/rules b/debian/rules
index 73855d8..edbbc3e 100755
--- a/debian/rules
+++ b/debian/rules
@@ -84,6 +84,7 @@ binary-arch: install
dh_installchangelogs -a ChangeLog
dh_installdocs -a
+ dh_installdebconf -a
install -d debian/dpkg/usr/share/lintian/overrides
install -m 644 debian/dpkg.lintian-overrides \
@@ -131,6 +132,7 @@ clean:
rm -rf build-tree
dh_clean
+ debconf-updatepo
.PHONY: build install binary-arch binary-indep binary clean
diff --git a/lib/dpkg.h b/lib/dpkg.h
index 7e2e428..10fbb46 100644
--- a/lib/dpkg.h
+++ b/lib/dpkg.h
@@ -146,6 +146,8 @@
#define FIND "find"
#define SHELL "sh"
#define DIFF "diff"
+#define DEBCONF "debconf"
+#define DEBCONF_PROMPTCONF "/usr/share/dpkg/dpkg-confprompt.sh"
#define SHELLENVIR "SHELL"
diff --git a/scripts/dpkg-confprompt.sh b/scripts/dpkg-confprompt.sh
new file mode 100755
index 0000000..2e9b2d0
--- /dev/null
+++ b/scripts/dpkg-confprompt.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+# internal script for debconf-based management of conffiles
+# calling convention:
+# dpkg-confprompt.sh <pkg> <cfgfile> "<reason>"
+# initial implementation by sean finney
+# copyright (c) 2008 sean finney <seanius@debian.org>
+# this file may be used in accordance with the dpkg software copyright
+
+
+# debug function
+say(){
+ [ -z "$DPKG_DEBCONF_DEBUG" ] || echo "$@" >&2
+}
+
+# properly escape newlines and other junk from pre-translated strings
+# sent to us by the dpkg program
+escape(){
+ local foo nonempty
+ echo "$1" | sed -e 's/^ *//g' -e 's/ *$//g' -e 's/ *==> *//g' | \
+ while read foo; do
+ if [ -n "$foo" ]; then
+ if [ -n "$nonempty" ]; then
+ printf "\\\\n"
+ else
+ nonempty="yup"
+ fi
+ printf "$foo"
+ fi
+ done
+ echo
+}
+
+# read the response from the debconf system, return the debconf status code
+# the (global) variable res stores the resulting string
+readres(){
+ local status
+ read status res
+ say "readres = status: $status res: $res"
+ return $status
+}
+
+pkg="$1"
+file="$2"
+reason="`escape \"$3 \"`"
+# XXX maybe we don't want this
+#defact="`escape \"$3 \"`"
+
+# force-register the templates, since dpkg will not do this itself due
+# to not using debconf in any of the maintainer scripts
+echo x_loadtemplatefile /var/lib/dpkg/info/dpkg.templates dpkg
+readres || exit 255
+
+# reset the value, since we share this template across multiple packages
+# and conffiles the stored value is worthless
+echo reset dpkg/promptconfaction
+readres || exit 255
+
+# perform some substitutions
+echo subst dpkg/promptconfaction pkg "$pkg"
+readres || exit 255
+echo subst dpkg/promptconfaction file "$file"
+readres || exit 255
+echo subst dpkg/promptconfaction reason "$reason"
+readres || exit 255
+# XXX maybe we don't want this
+#echo subst dpkg/promptconfaction defact "$defact"
+#readres || exit 255
+
+# ask the answer and get the response
+echo input high dpkg/promptconfaction
+readres || exit 255
+echo go
+readres || exit 255
+echo get dpkg/promptconfaction
+readres || exit 255
+
+# based on the answer, return the integer value of the equivalent character,
+# had dpkg handled the question itself from the cmdline. any non-ascii value
+# (> 127) implies something has gone wrong internally.
+case $res in
+"Install the package maintainer's version")
+ code=121 # 'y'
+ ;;
+"Keep the currently installed version")
+ code=110 # 'n'
+ ;;
+"Show the differences between the current and new versions")
+ code=100 # 'd'
+ ;;
+"Invoke a shell to investigate")
+ code=122 # 'z'
+ ;;
+*)
+ say "wtf am i supposed to do with '$res'?"
+ code=255
+ ;;
+esac
+exit $code
diff --git a/src/configure.c b/src/configure.c
index c6cba8e..8da09cf 100644
--- a/src/configure.c
+++ b/src/configure.c
@@ -54,6 +54,8 @@ static void suspend(void);
static enum conffopt promptconfaction(const char* package, const char* cfgfile,
const char* realold, const char* realnew, int useredited,
int distedited, enum conffopt what);
+static int debconf_promptconf(const char *pkg, const char *conffile,
+ const char *reason);
void deferred_configure(struct pkginfo *pkg) {
/* The algorithm for deciding what to configure first is as follows:
@@ -571,7 +573,7 @@ static enum conffopt promptconfaction(const char* package, const char* cfgfile,
_("\n ==> Modified (by you or by a script) since installation.\n") :
_("\n ==> Deleted (by you or by a script) since installation.\n"));
- fprintf(stderr, distedited ?
+ reason = ( distedited ?
_(" ==> Package distributor has shipped an updated version.\n") :
_(" Version in package is the same as at last installation.\n"));
}
@@ -603,38 +605,45 @@ static enum conffopt promptconfaction(const char* package, const char* cfgfile,
cc = 'y';
break;
}
- }
-
-
- fprintf(stderr,
+ }
+
+ /* try to use debconf to resolve the problem */
+ cc = debconf_promptconf(package, cfgfile, reason);
+ /* if 127 or higher is returned then there was a problem executing
+ * the script (or other internall error) and we fall back to the
+ * oldschool way */
+ if (cc >= 127) {
+ cc= 0;
+ fprintf(stderr,
_(" What would you like to do about it ? Your options are:\n"
" Y or I : install the package maintainer's version\n"
" N or O : keep your currently-installed version\n"
" D : show the differences between the versions\n"
" Z : background this process to examine the situation\n"));
- if (what & cfof_keep)
- fprintf(stderr, _(" The default action is to keep your current version.\n"));
- else if (what & cfof_install)
- fprintf(stderr, _(" The default action is to install the new version.\n"));
+ if (what & cfof_keep)
+ fprintf(stderr, _(" The default action is to keep your current version.\n"));
+ else if (what & cfof_install)
+ fprintf(stderr, _(" The default action is to install the new version.\n"));
- s= strrchr(cfgfile,'/');
- if (!s || !*++s) s= cfgfile;
- fprintf(stderr, "*** %s (Y/I/N/O/D/Z) %s ? ",
- s,
- (what & cfof_keep) ? _("[default=N]") :
- (what & cfof_install) ? _("[default=Y]") : _("[no default]"));
+ s= strrchr(cfgfile,'/');
+ if (!s || !*++s) s= cfgfile;
+ fprintf(stderr, "*** %s (Y/I/N/O/D/Z) %s ? ",
+ s,
+ (what & cfof_keep) ? _("[default=N]") :
+ (what & cfof_install) ? _("[default=Y]") : _("[no default]"));
- if (ferror(stderr))
- ohshite(_("error writing to stderr, discovered before conffile prompt"));
+ if (ferror(stderr))
+ ohshite(_("error writing to stderr, discovered before conffile prompt"));
- cc= 0;
- while ((c= getchar()) != EOF && c != '\n')
- if (!isspace(c) && !cc) cc= tolower(c);
+ while ((c= getchar()) != EOF && c != '\n')
+ if (!isspace(c) && !cc) cc= tolower(c);
- if (c == EOF) {
- if (ferror(stdin)) ohshite(_("read error on stdin at conffile prompt"));
- ohshit(_("EOF on stdin at conffile prompt"));
+ if (c == EOF) {
+ if (ferror(stdin))
+ ohshite(_("read error on stdin at conffile prompt"));
+ ohshit(_("EOF on stdin at conffile prompt"));
+ }
}
if (!cc) {
@@ -673,3 +682,23 @@ static enum conffopt promptconfaction(const char* package, const char* cfgfile,
return what;
}
+
+static int debconf_promptconf(const char *pkg, const char *cfgfile,
+ const char *reason){
+ char *cmd=NULL;
+ size_t cmdsz=0;
+ int ret=0;
+
+ /* <debconf> <confpromptscript> <pkg> <cfgfile> "<reason>"\0 */
+ cmdsz = strlen(DEBCONF" "DEBCONF_PROMPTCONF" ")+strlen(pkg)+1
+ + strlen(cfgfile) + 2 + strlen(reason) + 2;
+ cmd = m_malloc(cmdsz);
+ snprintf(cmd, cmdsz, "%s %s %s %s \"%s\"", DEBCONF, DEBCONF_PROMPTCONF,
+ pkg, cfgfile, reason);
+ debug(dbg_conff, "debconf_promptconf %s %s = ", pkg, cfgfile);
+ ret=WEXITSTATUS(system(cmd));
+ if(ret <= 127) debug(dbg_conff, "%d ('%c')\n", ret, (char)ret);
+ else debug(dbg_conff, "%d\n", ret);
+ free(cmd);
+ return ret;
+}
--
1.5.4.3
Reply to: