[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

debconf support for dpkg [patch]



I've been working on adding rudimentary debconf support to dpkg. I'm
not tackling the use-of-debconf-in-postinst issue yet; I just want to make
dpkg run the debconf config script at an appropriate time -- namely right
before the postinst[1] script runs. Right now this is accomplished via
dreadful hackery in debconf, and we badly need a cleaner way of doing it.

First I had to decide how the config script will actually be called. It's
useful to assume that the config script always talks the debconf protocol,
so instead of dpkg calling the config script directly, debconf should be
passed the script to run, so it can start up, and run the script however it
needs to. It also needs the name of the package that is being configured,
and if there are templates, it needs to know where the template file is. So:

	debconf packagename -t /var/lib/dpkg/info/package.templates \
		/var/lib/dpkg/info/package.config args

/usr/bin/debconf will be responsible for running the program with the
args, in such a way that it can communicate with debconf using the
debconf protocol. It will probably be an alternative to allow cdebconf
or debconf to provide it. /usr/bin/debconf is a short program, so I've
attached it.

I have a patch (attached) that makes dpkg support this. Some examples:

1. A dummy package with a config script that just echos the arguments
   passed to it, to stderr.
a. new install

   root@gumdrop:/home/joey/tmp/dpkg-1.8.3.1/build/main>./dpkg -i
   /tmp/testpkg.deb 
   Selecting previously deselected package testpkg.
   (Reading database ... 66591 files and directories currently installed.)
   Unpacking testpkg (from /tmp/testpkg.deb) ...
   Setting up testpkg (0.5.55) ...
   !! Config script now running: configure

b. reinstall

   root@gumdrop:/home/joey/tmp/dpkg-1.8.3.1/build/main>./dpkg -i
   /tmp/testpkg.deb
   (Reading database ... 66591 files and directories currently installed.)
   Preparing to replace testpkg 0.5.55 (using /tmp/testpkg.deb) ...
   Unpacking replacement testpkg ...
   Setting up testpkg (0.5.55) ...
   !! Config script now running: configure 0.5.55

2. A dummy package with a config script that fails.

   root@gumdrop:/home/joey/tmp/dpkg-1.8.3.1/build/main>./dpkg -i /tmp/testpkg.deb
   (Reading database ... 66591 files and directories currently installed.)
   Preparing to replace testpkg 0.5.55 (using /tmp/testpkg.deb) ...
   Unpacking replacement testpkg ...
   Setting up testpkg (0.5.55) ...
   !! Config script now running: configure 0.5.55
   dpkg: error processing testpkg (--install):
    subprocess configuration script returned error exit status 1
   Errors were encountered while processing:
    testpkg
   + exit 1
   root@gumdrop:/home/joey/tmp/dpkg-1.8.3.1/build/main>dpkg -s testpkg
   Package: testpkg
   Status: install ok half-configured
   Priority: standard
   Section: admin
   Maintainer: Joey Hess <joeyh@debian.org>
   Version: 0.5.55
   Config-Version: 0.5.55
   Description: test package
    test package. remove me

2. A package with a config script that uses debconf to ask a question.

   root@gumdrop:~joey/tmp/dpkg-1.8.3.1/build/main>DEBIAN_FRONTEND=text DEBCONF_DEBUG=developer ./dpkg -i /tmp/testpkg.deb
   (Reading database ... 66591 files and directories currently installed.)
   Preparing to replace testpkg 0.5.55 (using /tmp/testpkg.deb) ...
   Unpacking replacement testpkg ...
   Setting up testpkg (0.5.55) ...
   debconf (developer): debconf started
   debconf (developer): loading templates from /var/lib/dpkg/info/testpkg.templates
   debconf (developer): starting /var/lib/dpkg/info/testpkg.config configure 0.5.55
   debconf (developer): <-- FSET testpkg/bar seen false
   debconf (developer): --> 0 false
   debconf (developer): <-- INPUT critical testpkg/bar
   debconf (developer): --> 0 question will be asked
   debconf (developer): <-- GO 
   Configuring Testpkg
   -------------------
   
   This is a test question. 
   
   Did you see this? no
   
   debconf (developer): --> 0 ok

Comments welcome.

-- 
see shy jo

[1] I earlier thought before the preinst would be best, but we need to make
    sure the package's dependancy on debconf is fullfilled before the config
    script is run. Also, just as for postinst scripts, there is no
    error-unwind for config scripts, and it makes a great deal of sense
    for dpkg --configure package to do the debconf configuration too,
    and for a package to be in a "half-configured" state if the config
    script fails.
[2] Debconf does deal with it, via some absurdly ugly hackery. I'll worry
    about doing away with that nastiness later; one peice at a time..
#!/usr/bin/perl -w

=head1 NAME

debconf - runs a program under debconf

=head1 SYNOPSIS

 debconf owner [-t templates] program [params]

=head1 DESCRIPTION

This program runs a program that uses debconf, and talkes to it via the
debconf protocol, accessing the database and displaying questions as
directed.

If the -t flag is specified, the filename after it should be a debconf
templates file, which will be loaded first.

=head 1 RETURN VALUE

This program returns the return code of the program it runs.

=cut

sub usage {
	die "Usage: $0 template program args\n";
}

use strict;
use Debconf::Template;
use Debconf::ConfigDb qw(loaddb savedb registertemplates);
use Debconf::Config qw(dbdir);
use Debconf::AutoSelect qw(:all);
use Debconf::Log qw(:all);

# Parse parameters.
my (@templates, $package, @command);
# If only perl-base had getopt..
for (my $x=0; $x < @ARGV;) {
        $_=$ARGV[$x++];

	if (/^-(?:t|-templates)$/) {
		push @templates, $ARGV[$x++];
	}
	elsif (/^--?(\w+)/) {
		print STDERR "Unknown option: $1\n";
		usage();
	}
	elsif (! $package) {
		$package=$_;
	}
	else {
		push @command, $_;
	}
}
usage unless $package and @command;

# Initialize.
debug developer => "debconf started";
loaddb(dbdir());
my $frontend=make_frontend();
$frontend->default_title($package);

# Load templates.
foreach my $fn (@templates) {
	debug developer => "loading templates from $fn";
	registertemplates($package, Debconf::Template->load($fn));
}

# Start up the confmodule we were asked to run.
my $confmodule=make_confmodule(join " ",@ARGV);

# Make sure any questions the confmodule generates are owned by this package.
$confmodule->owner($package);

# Talk to it until it is done.
1 while ($confmodule->communicate);

# End.
$frontend->shutdown;
savedb(dbdir());
exit $confmodule->exitcode;

=head1 AUTHOR

Joey Hess <joey@kitenet.net>

=cut
diff -ur old/dpkg-1.8.3.1/include/dpkg.h.in dpkg-1.8.3.1/include/dpkg.h.in
--- old/dpkg-1.8.3.1/include/dpkg.h.in	Fri Jan 12 08:02:12 2001
+++ dpkg-1.8.3.1/include/dpkg.h.in	Mon Feb  5 15:24:00 2001
@@ -60,6 +60,7 @@
 
 #define CONTROLFILE        "control"
 #define CONFFILESFILE      "conffiles"
+#define CONFIGFILE      "config"
 #define PREINSTFILE        "preinst"
 #define POSTINSTFILE       "postinst"
 #define PRERMFILE          "prerm"
@@ -120,6 +121,7 @@
 #define MD5SUM       "md5sum"
 #define DSELECT      "dselect"
 #define DPKG         "dpkg"
+#define DEBCONF      "debconf"
 
 #define TAR          "tar"
 #define GZIP         "gzip"
diff -ur old/dpkg-1.8.3.1/main/configure.c dpkg-1.8.3.1/main/configure.c
--- old/dpkg-1.8.3.1/main/configure.c	Thu Aug 24 13:34:56 2000
+++ dpkg-1.8.3.1/main/configure.c	Mon Feb  5 16:22:50 2001
@@ -435,6 +435,15 @@
 
   modstatdb_note(pkg);
 
+  if (maintainer_script_debconf(pkg, CONFIGFILE, "configuration", 
+			        "configure",
+			        informativeversion(&pkg->configversion)
+				? versiondescribe(&pkg->configversion,
+					        vdew_nonambig)
+				: "",
+				(char*)0))
+    putchar('\n');
+  
   if (maintainer_script_installed(pkg, POSTINSTFILE, "post-installation",
                                   "configure",
                                   informativeversion(&pkg->configversion)
diff -ur old/dpkg-1.8.3.1/main/depcon.c dpkg-1.8.3.1/main/depcon.c
--- old/dpkg-1.8.3.1/main/depcon.c	Fri Jan 12 08:02:12 2001
+++ dpkg-1.8.3.1/main/depcon.c	Mon Feb  5 13:17:13 2001
@@ -42,7 +42,7 @@
                             struct pkginfo *dependedon,
                             struct deppossi *possi) {
   struct cyclesofarlink *sol;
-  const char *postinstfilename;
+  const char *postinstfilename, *configfilename;
   struct stat stab;
 
   /* We're investigating the dependency `possi' to see if it
@@ -57,7 +57,7 @@
   
   debug(dbg_depcon,"found cycle");
   /* Right, we now break one of the links.  We prefer to break
-   * a dependency of a package without a postinst script, as
+   * a dependency of a package without a postinst or config script, as
    * this is a null operation.  If this is not possible we break
    * the other link in the recursive calling tree which mentions
    * this package (this being the first package involved in the
@@ -68,13 +68,18 @@
   sofar= thislink;
   for (sol= sofar; !(sol != sofar && sol->pkg == dependedon); sol=sol->back) {
     postinstfilename= pkgadminfile(sol->pkg,POSTINSTFILE);
+    configfilename= pkgadminfile(sol->pkg,CONFIGFILE);
     if (lstat(postinstfilename,&stab)) {
       if (errno == ENOENT) break;
       ohshite(_("unable to check for existence of `%.250s'"),postinstfilename);
     }
+    if (lstat(configfilename,&stab)) {
+      if (errno == ENOENT) break;
+      ohshite(_("unable to check for existence of `%.250s'"),configfilename);
+    }
   }
-  /* Now we have either a package with no postinst, or the other
-   * occurrence of the current package in the list.
+  /* Now we have either a package with no postinst or config script, or the
+   * other occurrence of the current package in the list.
    */
   sol->possi->cyclebreak= 1;
   debug(dbg_depcon,"cycle broken at %s -> %s\n",
diff -ur old/dpkg-1.8.3.1/main/help.c dpkg-1.8.3.1/main/help.c
--- old/dpkg-1.8.3.1/main/help.c	Fri Jan 12 08:02:12 2001
+++ dpkg-1.8.3.1/main/help.c	Mon Feb  5 16:47:23 2001
@@ -291,6 +291,73 @@
   return 1;
 }
   
+int maintainer_script_debconf(struct pkginfo *pkg, const char *scriptname,
+                              const char *description, ...) {
+  /* all ...'s are const char*'s */
+  const char *scriptpath, *scriptexec, *template;
+  struct stat stab;
+  va_list ap;
+  int c1, instdirl, i;
+  char buf[100];
+  static char *arglist[PKGSCRIPTMAXARGS+1];
+  char *nextarg, *templatefile;
+  
+  /* Get template filename. */
+  template= pkgadminfile(pkg, "templates");
+  instdirl= strlen(instdir);
+  if (instdirl) {
+  	assert (strlen(template)>=instdirl);
+	template += instdirl;
+  }
+  templatefile=malloc(sizeof(char) * strlen(template) + 1);
+  strcpy(templatefile, template);
+  
+  sprintf(buf,"%s script",description);
+  scriptpath= pkgadminfile(pkg, scriptname);
+  va_start(ap, description);
+  scriptexec= preexecscript(scriptpath, ap);
+  
+  /* Build command line for execvp. */
+  i=0;
+  arglist[i++]= (char*)DEBCONF;
+  arglist[i++]= (char*)pkg->name;
+  if (stat(templatefile,&stab) == 0) {
+	  arglist[i++]= (char *)"-t";
+	  arglist[i++]= (char*)templatefile;
+  }
+  arglist[i++]= (char*)scriptexec;
+  for (;;) {
+    assert(i < PKGSCRIPTMAXARGS);
+    nextarg= va_arg(ap,char*);
+    if (!nextarg) break;
+    arglist[i++]= nextarg;
+  }
+  arglist[i]= 0;
+  va_end(ap);
+
+  if (stat(scriptpath,&stab)) {
+    free(templatefile);
+    if (errno == ENOENT) {
+      debug(dbg_scripts,"maintainer_script_installed nonexistent %s",scriptname);
+      return 0;
+    }
+    ohshite(_("unable to stat installed %s script `%.250s'"),description,scriptpath);
+  }
+  setexecute(scriptpath,&stab);
+  c1= m_fork();
+  if (!c1) {
+    execvp(DEBCONF,arglist);
+    ohshite(_("unable to execute %s"),buf);
+  }
+  free(templatefile);
+  script_catchsignals(); /* This does a push_cleanup() */
+  waitsubproc(c1,buf,0);
+  pop_cleanup(ehflag_normaltidy);
+
+  ensure_diversions();
+  return 1;
+}
+
 int maintainer_script_new(const char *scriptname, const char *description,
                           const char *cidir, char *cidirrest, ...) {
   char *const *arglist;
diff -ur old/dpkg-1.8.3.1/main/main.h dpkg-1.8.3.1/main/main.h
--- old/dpkg-1.8.3.1/main/main.h	Sun Nov  5 06:58:50 2000
+++ dpkg-1.8.3.1/main/main.h	Mon Feb  5 13:13:19 2001
@@ -182,6 +182,8 @@
 /* all ...'s are const char*'s ... */
 int maintainer_script_installed(struct pkginfo *pkg, const char *scriptname,
                                 const char *description, ...);
+int maintainer_script_debconf(struct pkginfo *pkg, const char *scriptname,
+			      const char *description, ...);
 int maintainer_script_new(const char *scriptname, const char *description,
                           const char *cidir, char *cidirrest, ...);
 int maintainer_script_alternative(struct pkginfo *pkg,

Reply to: