Package: debconf
Version: 1.5.19
Severity: normal
Tags: patch
User: [EMAIL PROTECTED]
Usertags: origin-ubuntu ubuntu-patch hardy

Ian Jackson pointed out that dpkg-reconfigure doesn't acquire the dpkg
lock while running maintainer scripts, and should; otherwise it could
race with a simultaneous 'dpkg -i' provided that the other package
doesn't use debconf, and bad things could conceivably happen. This will
become of more practical importance once dpkg supports triggers, because
then dpkg-reconfigure ought to arrange to process any triggers activated
by the maintainer scripts, and some confusion would arise if dpkg were
running at the same time.

How does this patch look? I thought it was big enough that I ought to
post it for review rather than just committing.

Thanks,

-- 
Colin Watson                                       [EMAIL PROTECTED]
Index: dpkg-reconfigure
===================================================================
--- dpkg-reconfigure	(revision 2265)
+++ dpkg-reconfigure	(working copy)
@@ -87,6 +87,7 @@
 use Debconf::Config;
 use Debconf::AutoSelect qw(:all);
 use Debconf::Log qw(:all);
+use Debconf::Lock;
 
 # Use low priority unless an option below overrides.
 Debconf::Config->priority('low');
@@ -114,6 +115,8 @@
 	exit 1;
 }
 
+my $dpkg_lock = acquire_dpkg_lock;
+
 Debconf::Db->load;
 
 if ($default_priority) {
@@ -241,6 +244,8 @@
 
 Debconf::Db->save;
 
+close $dpkg_lock;
+
 # Returns a list of all installed packages.
 sub allpackages {
 	my @ret;
Index: Debconf/Lock.pm
===================================================================
--- Debconf/Lock.pm	(revision 0)
+++ Debconf/Lock.pm	(revision 0)
@@ -0,0 +1,96 @@
+#!/usr/bin/perl -w
+
+=head1 NAME
+
+Debconf::Lock - dpkg lock handling for debconf
+
+=cut
+
+package Debconf::Lock;
+use strict;
+use POSIX;
+use Fcntl qw(:DEFAULT :flock :seek);
+use Debconf::Gettext;
+use Debconf::Log qw(warn);
+use base qw(Exporter);
+our @EXPORT = qw(acquire_dpkg_lock);
+
+# Translated messages throughout this file are deliberately at least similar
+# to dpkg's own (if not always identical), to help out overworked
+# translators.
+
+=head1 DESCRIPTION
+
+This module acquires the dpkg lock, so that certain debconf operations may
+be performed safely.
+
+The acquire_dpkg_lock symbol is exported by default.
+
+=head1 METHODS
+
+=over 4
+
+=cut
+
+sub linux_struct_flock {
+	my $type = shift;
+	my $whence = shift;
+
+	# On Linux, l_start and l_len might be either 4 bytes or 8 bytes
+	# depending on how perl was compiled. Since everything from l_start
+	# onwards will be zero, we just say 8 bytes for both and hope. (In
+	# other words, strictly speaking the pack format should be 's2l2i'.)
+	return pack('s2l4i', $type, $whence, 0, 0, 0, 0, 0);
+}
+
+=item acquire_dpkg_lock
+
+Acquire the dpkg lock. Returns a filehandle open on the lock file; to
+release the lock, either close this filehandle or simply allow the process
+to exit.
+
+On some operating systems, we may not yet know how to construct the lock
+structure to acquire the lock. Since debconf is often rather critical to get
+the system running, we simply warn about this and return undef.
+
+=cut
+
+sub acquire_dpkg_lock {
+	my $struct_flock;
+	if ($^O =~ /linux/) {
+		$struct_flock = \&linux_struct_flock;
+	} else {
+		warn gettext("don't know how to acquire dpkg lock on this system");
+		return undef;
+	}
+
+	my $lockfile = '/var/lib/dpkg/lock';
+
+	my $oldumask = umask;
+	umask($oldumask | 007);
+	open(DPKGLOCK, '+>', $lockfile)
+		or die gettext("unable to open/create status database lockfile")."\n";
+	umask($oldumask);
+	# rely on default $^F for close-on-exec
+
+	my $flock = $struct_flock->(F_WRLCK, SEEK_SET);
+	unless (fcntl(DPKGLOCK, F_SETLK, $flock)) {
+		if ($! == &POSIX::EACCES or $! == &POSIX::EAGAIN) {
+			die sprintf(gettext("status database area is locked by another process"))."\n";
+		} else {
+			die sprintf(gettext("unable to lock dpkg status database"))."\n";
+		}
+	}
+
+	return \*DPKGLOCK;
+}
+
+=back
+
+=head1 AUTHOR
+
+Colin Watson <[EMAIL PROTECTED]>
+
+=cut
+
+1

Reply via email to