2 # Copyright © 2007-2008 Anders Kaseorg <andersk@mit.edu> and
3 # Tim Abbott <tabbott@mit.edu>
4 # Copyright © 2011-2012 Geoffrey Thomas <geofft@mit.edu>
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License as
8 # published by the Free Software Foundation; either version 2, or (at
9 # your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 dh_configpackage - add maintainer script rules to displace, hide, or transform files
29 use Debian
::Debhelper
::Dh_Lib
;
30 use Debian
::Debhelper
::config_package
;
37 B<dh_configpackage> [B<--displace> I<path>] [B<--hide> I<path>] [B<--undisplace> I<path>] [B<--unhide> I<file>] [B<--transform> I<transformation>] [S<I<debhelper options>>] [B<-n>]
41 B<dh_configpackage> is a debhelper program to create "configuration
42 packages". These packages provide an ideal way to distribute
43 configurations to target systems while still affording local system
44 administrators a degree of control over their workstations. The
45 motivation and philosophy behind this style of packaging is described
46 in detail on the config-package-dev website. Configuration packages
47 make use of dpkg diversions and maintainer script snippets to provide
48 three primary operations: displacing, hiding, and transforming files.
50 The I<displace> operation consists of replacing a file on the target
51 system. The original file is renamed out of the way and diverted in the
52 dpkg database. The replacement file is then installed by the package,
53 and the config-package-dev maintainer script snippets create a symlink
54 from the original name. A common use of this is to install a wrapper
55 script for an executable.
57 The I<transform> operation is a special case of the displace operation.
58 At build time, a "transform script" is applied to the original source,
59 and the result is used as the replacement in the displace operation. A
60 common use of this is to change one value in a config file without
61 needing to re-type the entire config file (and risk bit-rot).
63 The I<hide> operation is yet another special case of the displace
64 operation, namely that there is no replacement or symlink. Instead, the
65 file is diverted to a unique path on the target system, thus preserving
66 its contents. A common use of this is to suppress a snippet file in a
67 configuration directory (e.g. /etc/foo.d), thus disabling a specific
68 operation or configuration.
70 The I<displace extension> is a suffix appended to the diverted versions
71 of files, and this suffix plus the string "-orig" is appended to the
72 original versions of the files. The default value is the first word of
73 the package name. For example, the extension for debathena-bin-example
74 would be ".debathena". So if debathena-bin-example displaced /bin/true,
75 the original /bin/true would be found at /bin/true.debathena-orig and
76 the new version (installed by e.g. dh_install) found at
77 /bin/true.debathena. /bin/true itself would become a symbolic link.
78 (For the remainder of this documentation, ".debathena" will be used as
79 the displace extension.)
85 =item debian/I<package>.displace
87 List the files to displace, one per line, including the full path and
88 displace extension. For example, to displace /usr/bin/true to
89 /usr/bin/true.debathena, you would list "/usr/bin/true.debathena" in
90 the file. (As with other Debhelper commands, you can omit the initial
91 leading slash in pathnames in the package, but these examples retain
94 =item debian/I<package>.hide
96 List the files to hide, one per line, including the full path and
97 displace extension. As noted above, these files won't actually be
98 removed, but merely diverted and renamed to a unique path below
99 /usr/share/I<package>.
101 =item debian/I<package>.undisplace
103 List the files to undisplace, one per line, including the full path and
104 displace extension. B<NOTE:> This is only needed when a new version of
105 the package no longer needs to displace a file (for example, if an
106 upstream bug was fixed). Packages automatically undo all operations
107 upon removal or deconfiguration.
109 =item debian/I<package>.unhide
111 List the files to unhide, one per line, including the full path
112 and displace extension. B<NOTE:> As with undisplace, this is only needed
113 when a new version of the package no longer needs to hide a file.
115 =item debian/I<package>.transform
117 Each line in the file specifies a transformation. A transformation
118 consists of two space-separated fields: the full path of the
119 target file including the displace extension and the transformation
120 command itself. The transformation can either be a single shell
121 command, or an executable file in the debian directory. The
122 transformation takes the original source of the file on stdin and prints
123 its transformation on stdout. Transformations are typically performed
124 by perl, sed, or awk, but there is no limitation on what can be used as
127 For example, to transform /etc/school.conf by replacing all
128 occurrences of the word 'Harvard' with the word 'MIT', you might
129 specify the following line:
131 /etc/school.conf.debathena sed -e 's/Harvard/MIT/g'
133 Or, storing the command in a separate script:
135 /etc/school.conf.debathena debian/transform_school.conf.pl
137 If the transformation script fails, the package build fails. You can use
138 this with e.g. Perl's C<or die> syntax to make sure that the source
139 file of the transformation has not changed from what you expected.
141 I<Transformation sources>: Under normal operation, the source (passed
142 on stdin) for the transformation is the name of the diversion without
143 the divert extension. In some cases, you may wish to use a different
144 source (e.g. a sample configuration file in /usr/share/doc). You can
145 specify this source as an optional field between the diversion
146 filename and the transformation. This field must begin with a '<'
147 immediately followed by the full path to the source. Taking the
148 example above, we might alter it as follows:
150 /etc/school.conf.debathena </usr/share/doc/school/conf.example sed -e 's/Harvard/MIT/g'
152 B<NOTE:> There is no "untransform" operation. Because a transform
153 operation is a special case of a displace operation, the "undisplace"
154 operation is the correct way of removing a no-longer-needed
155 transformation in future versions of the package.
157 =item debian/I<package>.displace-extension
159 This file is used to specify the displace extension for any files
160 diverted by this package, if you do not want to accept the default of
161 the first word in the package name. It will not normally be present.
170 =item B<-n>, B<--noscripts>
172 Do not modify maintainer scripts. This is a standard debhelper
173 option, though you are strongly discouraged from using it except for
174 debugging, as these operations rely heavily on the maintainer scripts.
176 =item B<--displace> I<path>
178 =item B<--hide> I<path>
180 =item B<--undisplace> I<path>
182 =item B<--unhide> I<path>
184 =item B<--transform> I<transformation>
186 These options allow for specifying an operation on the command line.
187 The argument to the option is the same as a single line of the
188 corresponding file, as described above. You may specify multiple
189 occurrences of B<--displace>, or you may invoke B<dh_configpackage>
190 repeatedly with different invocations. The most common use of this
191 format is in a rules file when performing conditional operations, in an
192 C<override_dh_configpackage> target in the C<rules> file. See the
193 debathena-conffile-example-1.1 package in
194 /usr/share/doc/config-package-dev/EXAMPLES for one such use.
200 my (@arg_displace, @arg_hide, @arg_undisplace, @arg_unhide, @arg_transform);
201 my $args_present = 0;
204 "displace=s" => \
@arg_displace,
205 "hide=s" => \
@arg_hide,
206 "undisplace=s" => \
@arg_undisplace,
207 "unhide=s" => \
@arg_unhide,
208 "transform=s" => \
@arg_transform,
211 if (@arg_displace or @arg_hide or @arg_undisplace or @arg_unhide or @arg_transform) {
215 # We default the displace extension to a period followed by the first
216 # word of the package name, on the assumption that it is probably the
217 # site name (e.g., "debathena-kerberos-config" displaces to
218 # ".debathena"). You can set this extension explicitly in
219 # debian/$package.displace-extension or debian/displace-extension.
220 sub displace_extension
{
222 my $file = pkgfile
($package, "displace-extension");
234 # Replace only the last instance of the displace extension in the
235 # filename, to make it possible to displace /path/foo.divert to
236 # foo.divert.divert-orig
237 sub displace_files_replace_name
{
238 my ($package, $filename, $replacement) = @_;
239 my $extension = displace_extension
($package);
240 $filename =~ s/(.*)\Q$extension\E/$1$replacement/;
244 # Encode a full path into the path it should be diverted to if it's
246 sub hide_files_name
{
247 my ($filename, $package) = @_;
248 return "/usr/share/$package/" . encode
($filename);
251 # At compatibility levels 6 and above, prerms take effect in the
252 # opposite order from postinsts
262 # check_file is used to verify that files on local disk have not
263 # been modified from the upstream packaged version.
265 # We check md5sums from both /var/lib/dpkg/info/$(package).md5sums
266 # (the md5sums database for non-conffiles) and the conffiles database
267 # used for prompting about conffiles being changed (via dpkg-query).
269 # There is some wrangling here because the formats of these sources differ.
273 my $truename = `dpkg-divert --truename $name`;
275 die "$truename missing\n" unless (-e
$truename);
276 my $package = `LC_ALL=C dpkg -S $name | sed -n '/^diversion by /! s/: .*\$// p'`;
278 die "$truename is not owned by any package\n" unless ($package);
280 my $ctx = Digest
::MD5
->new;
281 open(my $fh, $truename);
284 my $digest = $ctx->hexdigest;
290 open($fh, "-|", qw(dpkg-query --showformat=${Conffiles}\n --show), $package);
292 next unless /^ \Q$name\E ([0-9a-f]{32})$/;
297 die "md5sum mismatch on $name\n";
302 open(my $devnull, ">/dev/null");
303 my $pid = open3(undef, my $dpkg_query, $devnull, qw(dpkg-query --control-path), $package, "md5sums");
304 my $md5sums = <$dpkg_query>;
310 $md5sums ||= "/var/lib/dpkg/info/$package.md5sums";
318 next unless /^([0-9a-f]{32}) \Q$relname\E$/;
322 die "md5sum mismatch on $name\n";
329 die "$package contains no md5sums for $name. Is it a generated file?\n";
331 print "config-package-dev: warning: $package does not include md5sums!\n";
332 print "config-package-dev: warning: md5sum for $name not verified.\n";
339 foreach my $package (@
{$dh{DOPACKAGES
}}) {
340 my (@displacefiles, @hidefiles, @undisplacefiles, @unhidefiles, @transformfiles);
342 if (($package eq $dh{FIRSTPACKAGE
} || $dh{PARAMS_ALL
}) && $args_present) {
343 @displacefiles = @arg_displace;
344 @hidefiles = @arg_hide;
345 @undisplacefiles = @arg_undisplace;
346 @unhidefiles = @arg_unhide;
347 @transformfiles = map {[split]} @arg_transform;
349 my $displacefile = pkgfile
($package, "displace");
350 @displacefiles = filearray
($displacefile) if $displacefile;
351 my $hidefile = pkgfile
($package, "hide");
352 @hidefiles = filearray
($hidefile) if $hidefile;
353 my $undisplacefile = pkgfile
($package, "undisplace");
354 @undisplacefiles = filearray
($undisplacefile) if $undisplacefile;
355 my $unhidefile = pkgfile
($package, "unhide");
356 @unhidefiles = filearray
($unhidefile) if $unhidefile;
357 my $transformfile = pkgfile
($package, "transform");
358 @transformfiles = filedoublearray
($transformfile) if $transformfile;
361 my $tmp = tmpdir
($package);
362 my $extension = displace_extension
($package);
364 if (! $dh{ONLYSCRIPTS
} && @hidefiles) {
365 doit
("install", "-d", "$tmp/usr/share/$package");
368 foreach my $line (@transformfiles) {
369 my $file = shift @
$line;
372 if (@
$line[0] =~ /^</) {
373 $source = shift @
$line;
376 $source = displace_files_replace_name
($package, $file, "");
377 if ($source eq $file) {
378 die("Error: '$file' does not contain '$extension'\n");
382 #if ($rest =~ m|^debian/[^ ]*| && -e $rest) {
383 # # In case this is a single file in debian/, make sure it's
384 # # executable, since source-format 1.0 debian/ directories
385 # # (from .diff.gz) cannot have mode bits
389 $source = check_file
($source);
390 my $destdir = dirname
("$tmp/$file");
392 doit
("install", "-d", $destdir);
394 complex_doit
(@
$line, "<", $source, ">", "$tmp/$file");
395 push @displacefiles, $file;
398 # Add code to postinst to add/remove diversions as appropriate
399 if (! $dh{NOSCRIPTS
}) {
400 if (@undisplacefiles || @unhidefiles || @displacefiles || @hidefiles) {
401 my $postinst = escape_shell
(join "\\n", (
402 'if [ "$1" = "configure" ] || [ "$1" = "abort-remove" ]; then',
403 (map {" check_undisplace_unlink " . displace_files_replace_name
($package, $_, " ")} @undisplacefiles),
404 (map {" check_undisplace_unhide $_ " . hide_files_name
($_, $package)} @unhidefiles),
405 (map {" displace_link " . displace_files_replace_name
($package, $_, " ")} @displacefiles),
406 (map {" displace_hide $_ " . hide_files_name
($_, $package)} @hidefiles),
409 autoscript
($package, "postinst", "displace.sh.in",
410 "s/#PACKAGE#/$package/g; s/#DEB_DISPLACE_EXTENSION#/$extension/g; \\\$a\"$postinst\"");
412 if (@displacefiles || @hidefiles) {
413 my $prerm = escape_shell
(join "\\n", (
414 'if [ "$1" = "remove" ] || [ "$1" = "deconfigure" ]; then',
415 (map {" undisplace_unlink " . displace_files_replace_name
($package, $_, " ")} reverse_if_6
(@displacefiles)),
416 (map {" undisplace_unhide $_ $package"} reverse_if_6
(@hidefiles)),
419 autoscript
($package, "prerm", "displace.sh.in",
420 "s/#PACKAGE#/$package/g; s/#DEB_DISPLACE_EXTENSION#/$extension/g; \\\$a\"$prerm\"");
424 # Add an encoding of the names of the diverted files to the Provides:
425 # and Conflicts: lists. This prevents two packages diverting the same
426 # file from being installed simultaneously (it cannot work, and this
427 # produces a much less ugly error). Requires in debian/control:
428 # Provides: ${diverted-files}
429 # Conflicts: ${diverted-files}
430 foreach my $file (@displacefiles, @hidefiles) {
431 my $encodedfile = encode
(displace_files_replace_name
($package, $file, ""));
432 addsubstvar
($package, "diverted-files", "diverts-$encodedfile");
438 Because the displace extension is automatically generated from the
439 package name, renaming the package can have unintended consequences.
440 If you must rename a package such that the first component of the name
441 changes, specify the old extension using the C<displace-extension> file
446 L<debhelper(7)>, L<The config-package-dev
447 homepage|http://debathena.mit.edu/config-package-dev>
449 This program is a part of config-package-dev.
453 config-package-dev was written by Anders Kaseorg <andersk@mit.edu> and
454 Tim Abbott <tabbott@mit.edu>. The debhelper port is by Geoffrey Thomas
455 <geofft@mit.edu>. Documentation by Jonathan Reed <jdreed@mit.edu>.