diff options
Diffstat (limited to '')
-rwxr-xr-x | dh_install | 387 | ||||
-rwxr-xr-x | dh_installalternatives | 193 | ||||
-rwxr-xr-x | dh_installcatalogs | 138 | ||||
-rwxr-xr-x | dh_installchangelogs | 375 | ||||
-rwxr-xr-x | dh_installcron | 90 | ||||
-rwxr-xr-x | dh_installdeb | 429 | ||||
-rwxr-xr-x | dh_installdebconf | 243 | ||||
-rwxr-xr-x | dh_installdirs | 141 | ||||
-rwxr-xr-x | dh_installdocs | 447 | ||||
-rwxr-xr-x | dh_installemacsen | 149 | ||||
-rwxr-xr-x | dh_installexamples | 192 | ||||
-rwxr-xr-x | dh_installgsettings | 106 | ||||
-rwxr-xr-x | dh_installifupdown | 82 | ||||
-rwxr-xr-x | dh_installinfo | 133 | ||||
-rwxr-xr-x | dh_installinit | 427 | ||||
-rwxr-xr-x | dh_installinitramfs | 103 | ||||
-rwxr-xr-x | dh_installlogcheck | 91 | ||||
-rwxr-xr-x | dh_installlogrotate | 63 | ||||
-rwxr-xr-x | dh_installman | 430 | ||||
-rwxr-xr-x | dh_installmanpages | 208 | ||||
-rwxr-xr-x | dh_installmenu | 100 | ||||
-rwxr-xr-x | dh_installmime | 73 | ||||
-rwxr-xr-x | dh_installmodules | 119 | ||||
-rwxr-xr-x | dh_installpam | 81 | ||||
-rwxr-xr-x | dh_installppp | 78 | ||||
-rwxr-xr-x | dh_installsystemd | 453 | ||||
-rwxr-xr-x | dh_installsystemduser | 288 | ||||
-rwxr-xr-x | dh_installsysusers | 115 | ||||
-rwxr-xr-x | dh_installtmpfiles | 128 | ||||
-rwxr-xr-x | dh_installudev | 112 | ||||
-rwxr-xr-x | dh_installwm | 140 | ||||
-rwxr-xr-x | dh_installxfonts | 100 |
32 files changed, 6214 insertions, 0 deletions
diff --git a/dh_install b/dh_install new file mode 100755 index 0000000..da92715 --- /dev/null +++ b/dh_install @@ -0,0 +1,387 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_install - install files into package build directories + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_install> [B<-X>I<item>] [B<--autodest>] [B<--sourcedir=>I<dir>] [S<I<debhelper options>>] [S<I<file|dir> ... I<destdir>>] + +=head1 DESCRIPTION + +B<dh_install> is a debhelper program that handles installing files into package +build directories. There are many B<dh_install>I<*> commands that handle installing +specific types of files such as documentation, examples, man pages, and so on, +and they should be used when possible as they often have extra intelligence for +those particular tasks. B<dh_install>, then, is useful for installing everything +else, for which no particular intelligence is needed. It is a replacement for +the old B<dh_movefiles> command. + +This program may be used in one of two ways. If you just have a file or two +that the upstream Makefile does not install for you, you can run B<dh_install> +on them to move them into place. On the other hand, maybe you have a large +package that builds multiple binary packages. You can use the upstream +F<Makefile> to install it all into F<debian/tmp>, and then use B<dh_install> to copy +directories and files from there into the proper package build directories. + +From debhelper compatibility level 7 on, B<dh_install> will fall back to +looking in F<debian/tmp> for files, if it does not find them in the current +directory (or wherever you've told it to look using B<--sourcedir>). + +=head1 FILES + +=over 4 + +=item debian/I<package>.install + +List the files to install into each package and the directory they should be +installed to. The format is a set of lines, where each line lists a file or +files to install, and at the end of the line tells the directory it should be +installed in. The name of the files (or directories) to install should be given +relative to the current directory, while the installation directory is given +relative to the package build directory. You may use wildcards in the names of +the files to install. + +Note that if you list exactly one filename or wildcard-pattern on a line by +itself, with no explicit destination, then B<dh_install> +will automatically guess the destination to use, the same as if the +--autodest option were used. + +Supports substitution variables in compat 13 and later as +documented in L<debhelper(7)>. + +=item debian/not-installed + +Used with the deprecated B<--list-missing> and B<--fail-missing> options. +Please refer to L<dh_missing(1)> for the documentation of this file. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<--list-missing> + +B<Deprecated>: Please use B<dh_missing --list-missing> instead. If you use +this option, B<dh_install> will call B<dh_missing> with that option after it has +processed all the files. Please see L<dh_missing(1)> for the documentation of +this option. + +This option is removed in compat 12. + +=item B<--fail-missing> + +B<Deprecated>: Please use B<dh_missing --fail-missing> instead. If you use +this option, B<dh_install> will call B<dh_missing> with that option after it has +processed all the files. Please see L<dh_missing(1)> for the documentation of +this option. + +This option is removed in compat 12. + +=item B<--sourcedir=>I<dir> + +Look in the specified directory for files to be installed. + +Note that this is not the same as the B<--sourcedirectory> option used +by the B<dh_auto_>I<*> commands. You rarely need to use this option, since +B<dh_install> automatically looks for files in F<debian/tmp> in debhelper +compatibility level 7 and above. + +=item B<--autodest> + +Guess as the destination directory to install things to. If this is +specified, you should not list destination directories in +F<debian/package.install> files or on the command line. Instead, B<dh_install> +will guess as follows: + +Strip off F<debian/tmp> (or the sourcedir if one is given) from the front of +the filename, if it is present, and install into the dirname of the +filename. So if the filename is F<debian/tmp/usr/bin>, then that directory +will be copied to F<debian/package/usr/>. If the filename is +F<debian/tmp/etc/passwd>, it will be copied to F<debian/package/etc/>. + +=item I<file|dir> ... I<destdir> + +Lists files (or directories) to install and where to install them to. +The files will be installed into the first package F<dh_install> acts on. + +=back + +=cut + +init(options => { + "autodest" => \$dh{AUTODEST}, + "list-missing" => \$dh{LIST_MISSING}, + "fail-missing" => \$dh{FAIL_MISSING}, + "sourcedir=s" => \$dh{SOURCEDIR}, +}); + +my $srcdir = '.'; +if (defined($dh{SOURCEDIR})) { + $srcdir = $dh{SOURCEDIR}; + $srcdir =~ s{/+$}{}; + error("Invalid --sourcedir - must not be empty nor /") if not $srcdir; +} + +my $missing_files = 0; + +if ($dh{LIST_MISSING} || $dh{FAIL_MISSING}) { + deprecated_functionality('Please use dh_missing --list-missing/--fail-missing instead', 12); +} + +# Support for -X flag. +my $exclude = ''; +if ($dh{EXCLUDE_FIND}) { + $exclude = '! \( '.$dh{EXCLUDE_FIND}.' \)'; +} + +# PROMISE: DH NOOP WITHOUT pkgfile-logged(install) cli-options() + +foreach my $package (getpackages()) { + my (@installed, %dest2sources); + my $default_source_dir = default_sourcedir($package); + my @search_dirs = ($srcdir); + push(@search_dirs, $default_source_dir) if not compat(6); + + # Look at the install files for all packages to handle + # list-missing/fail-missing, but skip really installing for + # packages that are not being acted on. + my $skip_install = process_pkg($package) ? 0 : 1; + + my $tmp=tmpdir($package); + my $file=pkgfile($package,"install"); + + my @install; + if ($file) { + @install=filedoublearray($file); # no globbing here; done below + } + + + # With autodest, we can just pretend every pattern was on its own line + @install = map { [$_] } map { @$_ } @install if $dh{AUTODEST}; + + if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) { + if ($dh{AUTODEST}) { + # Same as above, with autodest, we can just isolate each entry + # - the split is for bug-backwards compatibility (#867866). + push(@install, map { [$_] } map { split } @ARGV); + } else { + # Bug backwards compatibility (#867866). The new "glob_expand" + # interface is smart enough to not split on spaces, but dh_install + # used to do that... *except* for the "DEST" since it was never + # passed to the glob function. + my @a = @ARGV; + my $dest = pop(@a) if @a > 1; + my @srcs = map { split } @a; + push(@srcs, $dest) if defined($dest); + push(@install, \@srcs); + } + } + + + my $glob_error_handler = sub { + # Do not require a match for packages that not acted on + # (directly). After all, the files might not have been + # generated/compiled. + return if $skip_install; + ++$missing_files; + goto \&glob_expand_error_handler_warn_and_discard; + }; + + foreach my $set (@install) { + my ($dest, @filelist, @patterns); + + if (@$set > 1) { + $dest=pop @$set; + } + # Skip excluded patterns. We will need two exclude checks per pattern; + # 1) exclude the entire pattern as people expect this to work (#814856) + # 2) exclude files matched by the pattern as people could have just + # excluded a single file of a "dir/*"-pattern. + # This line below filters entire patterns + @patterns = grep { not excludefile($_) } @{$set}; + next if not @patterns; + foreach my $glob (@patterns) { + my @found = glob_expand(\@search_dirs, $glob_error_handler, $glob); + push(@filelist, map { tr{/}{/}s; $_ } @found); + } + + if (! @filelist && ! $skip_install) { + warning("$package missing files: @$set"); + ++$missing_files; + next; + } + + # Do a quick bulk handling of excluded files and update @installed. + # - this is for filtering files matched by the pattern + @filelist = grep { not excludefile($_) } @filelist if $exclude; + push(@installed, @filelist); + + # ... because then we can short-curcit here. + next if $skip_install or $missing_files; + + if (not $exclude) { + my @unoptimized; + for my $src (@filelist) { + my $d = $dest // compute_dest($default_source_dir, $src); + my $basename = basename($src); + if (exists($dest2sources{$d}{$basename})) { + # If there is a clash, silently undo the optimizations. + # See #866405 and #868169. + my $replaced = delete($dest2sources{$d}{$basename}); + # Associate the $replaced the destination + # directory. We cannot be sure that compute_dest will + # get it right nor can we blindly set $dest. + # + # It is technically unnecessary for $src, but we + # might as well do it to possibly save a + # compute_dest call. + push(@unoptimized, [$replaced, $d], [$src, $d]); + next; + } + $dest2sources{$d}{$basename} = $src; + } + next if not @unoptimized; + @filelist = @unoptimized; + } + + foreach my $src (@filelist) { + + my $target_dest; + + if (ref($src)) { + # On a failed optimization, we will have the + # destination directory. + ($src, $target_dest) = @{$src}; + } else { + $target_dest = $dest; + if (! defined $target_dest) { + # Guess at destination directory. + $target_dest = compute_dest($default_source_dir, $src); + } + } + + # Make sure the destination directory exists. + install_dir("$tmp/$target_dest"); + + if (-d $src && $exclude) { + my $basename = basename($src); + my $dir = ($basename eq '.') ? $src : "$src/.."; + my $pwd=`pwd`; + chomp $pwd; + complex_doit("cd '$dir' && " . + "find '$basename' $exclude \\( -type f -or -type l \\) -print0 | LC_ALL=C sort -z | " . + "xargs -0 -I {} cp --reflink=auto --parents -dp {} $pwd/$tmp/$target_dest/"); + # cp is annoying so I need a separate pass + # just for empty directories + complex_doit("cd '$dir' && " . + "find '$basename' $exclude \\( -type d -and -empty \\) -print0 | LC_ALL=C sort -z | " . + "xargs -0 -I {} cp --reflink=auto --parents -a {} $pwd/$tmp/$target_dest/"); + } + else { + doit("cp", '--reflink=auto', "-a", $src, "$tmp/$target_dest/"); + } + } + } + + for my $dest (sort(keys(%dest2sources))) { + my @srcs = sort(values(%{$dest2sources{$dest}})); + # Make sure the destination directory exists. + install_dir("$tmp/$dest"); + xargs(\@srcs, "cp", '--reflink=auto', "-a", XARGS_INSERT_PARAMS_HERE, "$tmp/$dest/"); + } + log_installed_files($package, @installed); +} + +if ($missing_files) { + # There were files we could not install (e.g. patterns that matched nothing) + error("missing files, aborting"); +} + +if ($dh{LIST_MISSING} || $dh{FAIL_MISSING}) { + my @options; + foreach (@{$dh{EXCLUDE}}) { + push(@options, '--exclude', $_); + } + push(@options, '--sourcedir', $dh{SOURCEDIR}) if defined($dh{SOURCEDIR}); + push @options, "--list-missing" if $dh{LIST_MISSING}; + push @options, "--fail-missing" if $dh{FAIL_MISSING}; + doit("dh_missing", @options); +} + +sub compute_dest { + my ($source_dir, $dest) = @_; + + $dest =~ s/^(.*\/)?\Q$srcdir\E\///; + $dest =~ s/^(.*\/)?\Q$source_dir\E\///; + $dest = dirname("/".$dest); + + return $dest; +} + +=head1 EXAMPLES + +Here are some small examples of configuration files for dh_install. + + # Install my-prog into usr/bin (as "usr/bin/my-prog") + my-prog usr/bin + + # Install a plugins directory into usr/share/my-prog + # (as "usr/share/my-prog/plugins/") + plugins usr/share/my-prog + + # Install a file with spaces in into usr/share/my-prog/data + # (as "usr/share/my-prog/data/my datafile with spaces.txt") + # ASSUMES COMPAT 13, where substitution patterns are available + my${Space}datafile${Space}with${Space}spaces.txt usr/share/my-prog/data + + # Install a library into the multi-arch lib directory + # ASSUMES COMPAT 13, where substitution patterns are available + build/output/libfrop*.so.* usr/lib/${DEB_HOST_MULTIARCH} + +=head1 LIMITATIONS + +B<dh_install> cannot rename files or directories, it can only install them +with the names they already have into wherever you want in the package +build tree. + +However, renaming can be achieved by using B<dh-exec> with compatibility level 9 or +later. An example debian/I<package>.install file using B<dh-exec> +could look like: + + #!/usr/bin/dh-exec + debian/default.conf => /etc/my-package/start.conf + +Please remember the following three things: + +=over 4 + +=item * The package must be using compatibility level 9 or later (see L<debhelper(7)>) + +=item * The package will need a build-dependency on dh-exec. + +=item * The install file must be marked as executable. + +=back + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installalternatives b/dh_installalternatives new file mode 100755 index 0000000..7bfbae5 --- /dev/null +++ b/dh_installalternatives @@ -0,0 +1,193 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installalternatives - install declarative alternative rules + +=cut + +use strict; +use warnings; +use constant LINE_PREFIX => ' ' . q{\\} . "\n "; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installalternatives> [S<I<debhelper options>>] + +=head1 DESCRIPTION + +B<dh_installalternatives> is a debhelper program that is responsible for +parsing the declarative alternatives format and insert the relevant +maintscripts snippets to interface with L<update-alternatives(1)> + +=head1 FILES + +=over 4 + +=item debian/I<package>.alternatives + +An example of the format: + + Name: editor + Link: /usr/bin/editor + Alternative: /usr/bin/vim.basic + Dependents: + /usr/share/man/man1/editor.1.gz editor.1.gz /usr/share/man/man1/vim.1.gz + /usr/share/man/fr/man1/editor.1.gz editor.fr.1.gz /usr/share/man/fr/man1/vim.1.gz + /usr/share/man/it/man1/editor.1.gz editor.it.1.gz /usr/share/man/it/man1/vim.1.gz + /usr/share/man/pl/man1/editor.1.gz editor.pl.1.gz /usr/share/man/pl/man1/vim.1.gz + /usr/share/man/ru/man1/editor.1.gz editor.ru.1.gz /usr/share/man/ru/man1/vim.1.gz + Priority: 50 + +The fields B<Link>, B<Name>, B<Alternative>, and B<Priority> are mandatory and correspond +to the L<update-alternatives(1)> B<--install> parameters B<link>, B<name>, B<path>, and +B<priority> respectively. + +The B<Dependents> field is optional and consists of one or more lines. Each non-empty +line must contain exactly 3 space separated values that match (in order) the values passed +to the B<--slave> parameter for L<update-alternatives(1)>. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-n>, B<--no-scripts> + +Do not modify F<postinst>/F<postrm>/F<prerm> scripts. + +=back + +=cut + +init(); + +# Explicitly discard attempts to use --name; it does not make sense for +# this helper. +if ($dh{NAME}) { + warning('Ignoring unsupported --name option'); +} +$dh{NAME} = undef; + +# PROMISE: DH NOOP WITHOUT alternatives cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp = tmpdir($package); + my $alternatives = pkgfile($package, 'alternatives'); + + if (-f $alternatives) { + _parse_alternatives_file_and_generate_maintscripts($package, $tmp, $alternatives); + } +} + +sub _parse_alternative_and_generate_maintscript { + my ($package, $tmpdir, $alternatives_file, $ctrl) = @_; + + my $link_name = $ctrl->{'Name'} // error("Missing mandatory \"Name\" field in ${alternatives_file}"); + my $link_path = $ctrl->{'Link'} + // error("Missing mandatory \"Link\" field for \"${link_name}\" in ${alternatives_file}"); + my $impl_path = $ctrl->{'Alternative'} + // error("Missing mandatory \"Alternative\" field for \"${link_name}\" in ${alternatives_file}"); + my $priority = $ctrl->{'Priority'} + // error("Missing mandatory \"Priority\" field for \"${link_name}\" in ${alternatives_file}"); + my %maintscript_options; + + if (index($link_name, '/') > -1) { + error(qq{Invalid link name "${link_name}" in "${alternatives_file}": Must not contain slash}); + } + my $actual_impl_path = "${tmpdir}/${impl_path}"; + if ( ! -l $actual_impl_path && ! -e _) { + error(qq{Alternative "${impl_path}" for "${link_name}" in ${alternatives_file} does not exist in ${tmpdir}}); + } + if ( -d $actual_impl_path) { + error(qq{Alternative "${impl_path}" for "${link_name}" in ${alternatives_file} is a directory}); + } + if ($link_name eq $impl_path) { + error(qq{The link name cannot be the same as the implementation path "${link_name}" (in "${alternatives_file}")}); + } + + $maintscript_options{'RM_OPTIONS'} = "--remove ${link_name} ${impl_path}"; + $maintscript_options{'INSTALL_OPTIONS'} = "--install ${link_path} ${link_name} ${impl_path} ${priority}"; + + if (defined(my $slave_link_text = $ctrl->{'Dependents'})) { + my (%dlink_dup, @dependent_links); + for my $line (split(/\n/, $slave_link_text)) { + my ($dlink_name, $dlink_path, $dimpl_path, $trailing); + my $error_with_def = 0; + $line =~ s/^\s++//; + $line =~ s/\s++$//; + next if $line eq ''; # Ignore empty lines + ($dlink_path, $dlink_name, $dimpl_path, $trailing) = split(' ', $line, 4); + if (not $dlink_name) { + warning(qq{Missing link name value (2nd item) for dependent link "${dlink_name}" for "${link_name}"} + . qq{ in "${alternatives_file}"}); + $error_with_def = 1; + } elsif (index($dlink_name, '/') > -1) { + warning(qq{Invalid dependent link name "${dlink_name}" for "${link_name}"} + . qq{ in "${alternatives_file}": Must not contain slash}); + $error_with_def = 1; + } elsif ($dlink_dup{$dlink_name}) { + warning(qq{Dependent link "${dlink_name}" is seen more than once for "${link_name}"} + . qq{ in ${alternatives_file}}); + $error_with_def = 1; + } + if (not $dimpl_path) { + warning(qq{Missing path (alternative) value (3rd item) for dependent link "${dlink_name}"} + . qq{ for "${link_name}" in "${alternatives_file}"}); + $error_with_def = 1; + } + if ($dlink_name eq $dimpl_path) { + warning(qq{The link name cannot be the same as the implementation path for "${dlink_name}"} + . qq{ in "${alternatives_file}"}); + $error_with_def = 1; + } + if ($trailing) { + warning(qq{Trailing information for dependent link "${dlink_name}" for "${link_name}"} + . qq{ in "${alternatives_file}"}); + warning("Dependent links must consist of exactly 3 space-separated values"); + $error_with_def = 1; + } + if ($error_with_def) { + my $link_id = $dlink_name // ('no ' . (scalar(@dependent_links) + 1)); + error("Error parsing dependent link ${link_id} for \"${link_name}\" in ${alternatives_file}."); + } + push(@dependent_links, "--slave $dlink_path $dlink_name $dimpl_path"); + } + error("Empty \"Dependents\" field for \"${link_name}\" in ${alternatives_file} (please remove it or add an entry)") + if not @dependent_links; + $maintscript_options{'INSTALL_OPTIONS'} .= LINE_PREFIX . join(LINE_PREFIX, @dependent_links); + } + for my $wrong_name (qw(Slave Slaves Slave-Links)) { + if ($ctrl->{$wrong_name}) { + error("Please use Dependents instead of ${wrong_name}"); + } + } + + autoscript($package, 'postinst', 'postinst-alternatives', \%maintscript_options); + autoscript($package, 'prerm', 'prerm-alternatives', \%maintscript_options); + return; +} + +sub _parse_alternatives_file_and_generate_maintscripts { + my ($package, $tmpdir, $alternatives_file) = @_; + my ($ctrl, $fd); + require Dpkg::Control::HashCore; + open($fd, '<', $alternatives_file) or error("open $alternatives_file failed: $!"); + while (defined($ctrl = Dpkg::Control::HashCore->new) and ($ctrl->parse($fd, $alternatives_file))) { + _parse_alternative_and_generate_maintscript($package, $tmpdir, $alternatives_file, $ctrl); + } + close($fd); + return; +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=cut diff --git a/dh_installcatalogs b/dh_installcatalogs new file mode 100755 index 0000000..aa5f45d --- /dev/null +++ b/dh_installcatalogs @@ -0,0 +1,138 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installcatalogs - install and register SGML Catalogs + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +my $sgmlbasever = "1.28"; + +=head1 SYNOPSIS + +B<dh_installcatalogs> [S<I<debhelper options>>] [B<-n>] + +=head1 DESCRIPTION + +B<dh_installcatalogs> is a debhelper program that installs and +registers SGML catalogs. It complies with the Debian XML/SGML policy. + +Catalogs will be registered in a supercatalog, in +F</etc/sgml/I<package>.cat>. + +This command automatically adds maintainer script snippets for +registering and unregistering the catalogs and supercatalogs (unless +B<-n> is used). These snippets are inserted into the maintainer +scripts and the B<triggers> file by B<dh_installdeb>; see +L<dh_installdeb(1)> for an explanation of Debhelper maintainer script +snippets. + +A dependency on B<sgml-base> will be added to B<${misc:Depends}>, so be +sure your package uses that variable in F<debian/control>. + +=head1 FILES + +=over 4 + +=item debian/I<package>.sgmlcatalogs + +Lists the catalogs to be installed per package. Each line in that file +should be of the form C<I<source> I<dest>>, where I<source> indicates where the +catalog resides in the source tree, and I<dest> indicates the destination +location for the catalog under the package build area. I<dest> should +start with F</usr/share/sgml/>. + +Supports substitution variables in compat 13 and later as +documented in L<debhelper(7)>. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-n>, B<--no-scripts> + +Do not modify F<postinst>/F<postrm>/F<prerm> scripts nor add an +activation trigger. + +=back + +=head1 NOTES + +Note that this command is not idempotent. L<dh_prep(1)> should be +called between invocations of this command. Otherwise, it may cause +multiple instances of the same text to be added to maintainer scripts. + +=cut + +init(); + +# PROMISE: DH NOOP WITHOUT sgmlcatalogs cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmpdir = tmpdir($package); + my $sgmlcatlistfile = pkgfile($package, "sgmlcatalogs"); + my @sgmlinstalled; # catalogs we've installed + if ($#ARGV >= 0) { + error("extra command-line arguments"); + } + if ($sgmlcatlistfile) { + foreach my $line (filedoublearray($sgmlcatlistfile)) { + my $source = $line->[0]; + my $dest = $line->[1]; + my $fulldest = "$tmpdir/$dest"; + $fulldest =~ s|//|/|g; # beautification + + if (! -d dirname($fulldest)) { + # Ensure the parent exist + install_dir($tmpdir."/".dirname($dest)); + } + + install_file($source,$fulldest); + + push(@sgmlinstalled,$dest); + } + } + if (@sgmlinstalled) { + addsubstvar($package, "misc:Depends", "sgml-base", ">= $sgmlbasever"); + + install_dir("$tmpdir/etc/sgml"); + + my $centralcat = "/etc/sgml/$package.cat"; + + open(my $fd, ">", "$tmpdir$centralcat") || error("failed to write to $tmpdir$centralcat"); + foreach my $sgmldest (@sgmlinstalled) { + print {$fd} "CATALOG " . $sgmldest . "\n"; + } + close($fd) or error("close $tmpdir$centralcat: $!"); + + if (! $dh{NOSCRIPTS}) { + autotrigger($package, "activate-await", "update-sgmlcatalog"); + autoscript($package, "postrm", "postrm-sgmlcatalog", + { 'CENTRALCAT' => $centralcat }); + } + } + else { + # remove the dependency + addsubstvar($package, "misc:Depends", "sgml-base", ">= $sgmlbasever", 1); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +F</usr/share/doc/sgml-base-doc/> + +=head1 AUTHOR + +Adam Di Carlo <aph@debian.org> + +=cut diff --git a/dh_installchangelogs b/dh_installchangelogs new file mode 100755 index 0000000..49c3821 --- /dev/null +++ b/dh_installchangelogs @@ -0,0 +1,375 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installchangelogs - install changelogs into package build directories + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; +use Time::Piece; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installchangelogs> [S<I<debhelper options>>] [B<-k>] [B<-X>I<item>] [B<--no-trim>] [I<upstream>] + +=head1 DESCRIPTION + +B<dh_installchangelogs> is a debhelper program that is responsible for +installing changelogs into package build directories. + +An upstream F<changelog> file may be specified as an option. If none +is specified, B<dh_installchangelogs> may look for files with names +that seem likely to be changelogs as described in the next paragraphs. + +In non-native packages, B<dh_installchangelogs> will first look for +changelog files installed by the upstream build system into F<< +usr/share/doc/I<package> >> (of the package build directory) and +rename the most likely candidate (if any) to F<< +usr/share/doc/I<package>/changelog >>. Note that +B<dh_installchangelogs> does I<not> look into any source directory +(such as F<debian/tmp>). Otherwise, B<dh_installchangelogs> (at +compatibility level 7 or any later) will look for changelog files in +the source directory (e.g. the root or the F<docs> subdirectory). It +will look for F<changelog>, F<changes> and F<history> optionally with +common extensions (such as F<.txt>, F<.md> and F<.rst>). + +If a changelog file is specified and is an F<html> file (determined by file +extension), it will be installed as F<usr/share/doc/package/changelog.html> +instead. If the html changelog is converted to plain text, that variant +can be specified as a second parameter. When no plain text variant is +specified, a short F<usr/share/doc/package/changelog> is generated, +pointing readers at the html changelog file. + +The B<debchange>-style Debian changelogs are trimmed to include only +entries more recent than the release date of I<oldstable>. +No trimming will be performed if the B<--no-trim> option is passed or +if the B<DEB_BUILD_OPTIONS> environment variable contains B<notrimdch>. + +=head1 FILES + +=over 4 + +=item F<debian/changelog> + +=item F<debian/NEWS> + +=item debian/I<package>.changelog + +=item debian/I<package>.NEWS + +Automatically installed into usr/share/doc/I<package>/ +in the package build directory. + +Use the package specific name if I<package> needs a different +F<NEWS> or F<changelog> file. + +The F<changelog> file is installed with a name of changelog +for native packages, and F<changelog.Debian> for non-native packages. +The F<NEWS> file is always installed with a name of F<NEWS.Debian>. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-k>, B<--keep> + +Keep the original name of the upstream changelog. This will be accomplished +by installing the upstream changelog as F<changelog>, and making a symlink from +that to the original name of the F<changelog> file. This can be useful if the +upstream changelog has an unusual name, or if other documentation in the +package refers to the F<changelog> file. + +=item B<-X>I<item>, B<--exclude=>I<item> + +Exclude upstream F<changelog> files that contain I<item> anywhere in their +filename from being installed. + +Note that directory name of the changelog is also part of the match. + +=item B<--no-trim> + +Install the full changelog, not its trimmed version that includes only +recent entries. + +=item I<upstream> + +Install this file as the upstream changelog. + +=back + +=cut + +init(options => { + 'keep|k' => \$dh{K_FLAG}, + 'no-trim' => \$dh{NO_TRIM}, +}); + +my $news_name="NEWS.Debian"; +my $changelog_name="changelog.Debian"; + +use constant CUTOFF_DATE_STR => "2019-07-06"; # oldstable = Debian 10 Buster +use constant CUTOFF_DATE => Time::Piece->strptime(CUTOFF_DATE_STR, "%Y-%m-%d"); +use constant MIN_NUM_ENTRIES => 4; + +my $explicit_changelog = @ARGV ? 1 : 0; +my $default_upstream = $ARGV[0]; +my $default_upstream_text=$default_upstream; +my $default_upstream_html; +if (! defined($default_upstream)) { + if (! isnative($dh{MAINPACKAGE}) && !compat(6)) { + foreach my $dir (qw{. doc docs}) { + my $changelog = find_changelog($dir); + if ($changelog) { + $default_upstream = $changelog; + $default_upstream_text = $default_upstream; + last; + } + } + } + if (isnative($dh{MAINPACKAGE})) { + $changelog_name='changelog'; + } +} +elsif ($default_upstream=~m/\.html?$/i) { + $default_upstream_html=$default_upstream; + $default_upstream_text=$ARGV[1]; +} + +sub find_changelog { + my ($dir) = @_; + my @files=sort glob("$dir/*"); + foreach my $suffix ('', qw(.txt .md .rst)) { + foreach my $name (qw{changelog changes history}) { + my @matches=grep { + lc basename($_) eq "$name$suffix" && -f $_ && -s _ && ! excludefile($_) + } @files; + if (@matches) { + return shift(@matches); + } + } + } + return; +} + +sub install_debian_changelog { + my ($changelog, $package, $arch, $tmp) = @_; + + if ($dh{NO_TRIM} || get_buildoption("notrimdch")) { + # Install the whole changelog. + install_file($changelog, "$tmp/usr/share/doc/$package/$changelog_name"); + return; + } + + local $ENV{LC_ALL} = "C.UTF-8"; + + my $changelog_trimmed = generated_file($package, "dh_installchangelogs.dch.trimmed"); + my $changelog_binnmu = generated_file($package, "dh_installchangelogs.dch.binnmu"); + + open(my $log1, "<", $changelog) or error("Cannot open($changelog): $!"); + open(my $log2, ">", $changelog_trimmed) or error("Cannot open($changelog_trimmed): $!"); + + my $error_in_changelog = 0; + my $is_binnmu = 0; + my $entry = ""; + my $entry_num = 0; + while (my $line=<$log1>) { + $entry .= $line; + + # Identify binNUM packages by binary-only=yes in the first line of the changelog. + if (($. == 1) && ($line =~ /\A\S.*;.*\bbinary-only=yes/)) { + $is_binnmu = 1; + } + + # Get out of binNMU mode once we are in the second entry (and throw away one empty line). + if ($is_binnmu && ($entry_num eq 1)) { + $is_binnmu = 0; + $entry_num = 0; + $entry = ""; + next; + } + + if ($line =~ /^\s*--\s+.*?\s+<[^>]*>\s+(?<timestamp>.*)$/) { + if ($is_binnmu && ($entry_num eq 0)) { + # For binNMUs the first changelog entry is written into an extra file to + # keep the packages coinstallable. + open(my $log_binnum, ">", $changelog_binnmu) or error("Cannot open($changelog_binnmu): $!"); + print($log_binnum $entry) or error("Cannot write($changelog_binnmu): $!"); + close($log_binnum) or error("Cannot close($changelog_binnmu): $!"); + + # Continue processing the rest of the changelog. + $entry = ""; + $entry_num++; + next; + } + + my $timestamp = $+{timestamp}; + $timestamp =~ s/^[A-Za-z]+, +//; + + my $entry_time; + eval { $entry_time = Time::Piece->strptime($timestamp, '%d %b %Y %T %z') }; + if (! defined $entry_time) { + $error_in_changelog = 1; + warning("Could not parse timestamp '$timestamp'. $changelog will not be trimmed."); + truncate($log2, 0) or error("Cannot truncate($changelog_trimmed): $!"); + last; + } + + # Stop processing the changelog if we reached the cut-off date and + # at least MIN_NUM_ENTRIES entries have been added. + if (($entry_time < CUTOFF_DATE) && ($entry_num >= MIN_NUM_ENTRIES)) { + last; + } + + # Append entry to trimmed changelog. + print($log2 $entry) or error("Cannot write($changelog_trimmed): $!"); + $entry = ""; + $entry_num++; + } + } + # If the whole changelog has not been read, then it has been trimmed. + my $has_been_trimmed = !eof($log1); + + close($log1) or error("Cannot close($changelog): $!"); + close($log2) or error("Cannot close($changelog_trimmed): $!"); + + if ($error_in_changelog) { + # If the changelog could not be trimmed, fall back to the full changelog. + warning("$changelog could not be trimmed. The full changelog will be installed."); + $changelog_trimmed = $changelog; + } elsif ($has_been_trimmed) { + # Otherwise add a comment stating that this changelog has been trimmed. + my $note = "\n"; + $note .= "# Older entries have been removed from this changelog.\n"; + $note .= "# To read the complete changelog use `apt changelog $package`.\n"; + open(my $log2, ">>", $changelog_trimmed) or error("Cannot open($changelog_trimmed): $!"); + print($log2 $note) or error("Cannot write($changelog_trimmed): $!"); + close($log2) or error("Cannot close($changelog_trimmed): $!"); + } + + install_file($changelog_trimmed, "$tmp/usr/share/doc/$package/$changelog_name"); + if (-s $changelog_binnmu) { + install_file($changelog_binnmu, "$tmp/usr/share/doc/$package/$changelog_name.$arch"); + } +} + +on_pkgs_in_parallel { + foreach my $package (@_) { + next if is_udeb($package); + + my $tmp=tmpdir($package); + my $changelog=pkgfile($package,"changelog"); + my $news=pkgfile($package,"NEWS"); + my $upstream_changelog; + my ($upstream_changelog_text, $upstream_changelog_html); + my $changelog_from_tmp_dir = 0; + + if ($explicit_changelog) { + $upstream_changelog = $default_upstream; + $upstream_changelog_text = $default_upstream_text; + $upstream_changelog_html = $default_upstream_html; + } else { + # Check if the upstream build system provided a + # changelog + $upstream_changelog = find_changelog("${tmp}/usr/share/doc/${package}"); + if ($upstream_changelog) { + $upstream_changelog_text = $upstream_changelog; + $changelog_from_tmp_dir = 1; + } else { + $upstream_changelog = $default_upstream; + $upstream_changelog_text = $upstream_changelog; + } + } + + if (!$changelog) { + $changelog="debian/changelog"; + } + if (!$news) { + $news="debian/NEWS"; + } + + if (! -e $changelog) { + error("could not find changelog $changelog"); + } + + # If it is a symlink to a documentation directory from the same + # source package, then don't do anything. Think multi-binary + # packages that depend on each other and want to link doc dirs. + if (-l "$tmp/usr/share/doc/$package") { + my $linkval=readlink("$tmp/usr/share/doc/$package"); + my %allpackages=map { $_ => 1 } getpackages(); + if ($allpackages{basename($linkval)}) { + next; + } + # Even if the target doesn't seem to be a doc dir from the + # same source package, don't do anything if it's a dangling + # symlink. + next unless -d "$tmp/usr/share/doc/$package"; + } + + install_dir("$tmp/usr/share/doc/$package"); + + if (! $dh{NO_ACT}) { + my $arch = package_binary_arch($package); + install_debian_changelog($changelog, $package, $arch, $tmp); + } + + if (-e $news) { + install_file($news, "$tmp/usr/share/doc/$package/$news_name"); + } + + if (defined($upstream_changelog)) { + my $link_to; + my $base="$tmp/usr/share/doc/$package"; + if (defined($upstream_changelog_text)) { + if ($changelog_from_tmp_dir and not $dh{K_FLAG}) { + # mv (unless if it is the same file) + rename_path($upstream_changelog_text, "$base/changelog") + if basename($upstream_changelog_text) ne 'changelog'; + reset_perm_and_owner(0644, "$base/changelog"); + } else { + install_file($upstream_changelog_text, "$base/changelog"); + } + $link_to='changelog'; + } + if (defined($upstream_changelog_html)) { + if ($changelog_from_tmp_dir and not $dh{K_FLAG}) { + # mv (unless if it is the same file) + rename_path($upstream_changelog_html, "$base/changelog.html") + if basename($upstream_changelog_text) ne 'changelog.html'; + reset_perm_and_owner(0644, "$base/changelog.html"); + } else { + install_file($upstream_changelog_html,"$base/changelog.html"); + } + $link_to='changelog.html'; + if (! defined($upstream_changelog_text)) { + complex_doit("echo 'See changelog.html.gz' > $base/changelog"); + reset_perm_and_owner(0644,"$base/changelog"); + } + } + if ($dh{K_FLAG}) { + # Install symlink to original name of the upstream changelog file. + # Use basename in case original file was in a subdirectory or something. + doit('ln', '-sf', $link_to, "$tmp/usr/share/doc/$package/".basename($upstream_changelog)); + } + } + } +}; + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installcron b/dh_installcron new file mode 100755 index 0000000..9325b0e --- /dev/null +++ b/dh_installcron @@ -0,0 +1,90 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installcron - install cron scripts into etc/cron.* + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installcron> [S<B<debhelper options>>] [B<--name=>I<name>] + +=head1 DESCRIPTION + +B<dh_installcron> is a debhelper program that is responsible for installing +cron scripts. + +=head1 FILES + +=over 4 + +=item debian/I<package>.cron.daily + +=item debian/I<package>.cron.weekly + +=item debian/I<package>.cron.monthly + +=item debian/I<package>.cron.yearly + +=item debian/I<package>.cron.hourly + +=item debian/I<package>.cron.d + +Installed into the appropriate F<etc/cron.*/> directory in the package +build directory. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<--name=>I<name> + +Look for files named F<debian/package.name.cron.*> and install them as +F<etc/cron.*/name>, instead of using the usual files and installing them +as the package name. + +=back + +=cut + +init(); + +# PROMISE: DH NOOP WITHOUT cron.hourly cron.daily cron.weekly cron.monthly cron.yearly cron.d cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + foreach my $type (qw{hourly daily weekly monthly yearly}) { + my $cron=pkgfile($package,"cron.$type"); + if ($cron) { + install_dir("$tmp/etc/cron.$type"); + install_prog($cron,"$tmp/etc/cron.$type/".pkgfilename($package)); + } + } + # Separate because this needs to be mode 644. + my $cron=pkgfile($package,"cron.d"); + if ($cron) { + install_dir("$tmp/etc/cron.d"); + install_file($cron,"$tmp/etc/cron.d/".pkgfilename($package)); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installdeb b/dh_installdeb new file mode 100755 index 0000000..b9fd72b --- /dev/null +++ b/dh_installdeb @@ -0,0 +1,429 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installdeb - install files into the DEBIAN directory + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installdeb> [S<I<debhelper options>>] + +=head1 DESCRIPTION + +B<dh_installdeb> is a debhelper program that is responsible for installing +files into the F<DEBIAN> directories in package build directories with the +correct permissions. + +=head1 FILES + +=over 4 + +=item I<package>.postinst + +=item I<package>.preinst + +=item I<package>.postrm + +=item I<package>.prerm + +These maintainer scripts are installed into the F<DEBIAN> directory. + +B<dh_installdeb> will perform substitution of known tokens of +the pattern B<#TOKEN#>. In generally, scripts will want to +include the B<#DEBHELPER#> to benefit from the shell scripts +generated by debhelper commands (including those from +B<dh_installdeb> when it processes I<package>.maintscript files). + +The B<#DEBHELPER#> token should be placed on its own line as it is +often replaced by a multi-line shell script. + +=item I<package>.triggers + +=item I<package>.shlibs + +These control files are installed into the F<DEBIAN> directory. + +Note that I<package>.shlibs is only installed in compat level 9 and +earlier. In compat 10, please use L<dh_makeshlibs(1)>. + +=item I<package>.conffiles + +This file will be installed into the F<DEBIAN> directory. The +provided file will be enriched by debhelper to include all the +B<conffiles> auto-detected by debhelper (the maintainer should +not list there as debhelper assumes it should handle that part). + +This file is primarily useful for using "special" entries such as +the B<< remove-on-upgrade >> feature from dpkg. + +=item I<package>.maintscript + +Lines in this file correspond to L<dpkg-maintscript-helper(1)> +commands and parameters. However, the "maint-script-parameters" +should I<not> be included as debhelper will add those automatically. + +Example: + + # Correct + rm_conffile /etc/obsolete.conf 0.2~ foo + # INCORRECT + rm_conffile /etc/obsolete.conf 0.2~ foo -- "$@" + +In compat 10 or later, any shell metacharacters will be escaped, so +arbitrary shell code cannot be inserted here. For example, a line +such as C<mv_conffile /etc/oldconffile /etc/newconffile> will insert +maintainer script snippets into all maintainer scripts sufficient to +move that conffile. + +It was also the intention to escape shell metacharacters in previous +compat levels. However, it did not work properly and as such it was +possible to embed arbitrary shell code in earlier compat levels. + +The B<dh_installdeb> tool will do some basic validation of some of +the commands listed in this file to catch common mistakes. The +validation is enabled as a warning since compat 10 and as a hard +error in compat 12. + +Where possible, B<dh_installdeb> may choose to rewrite some or all +of the entries into equivalent features supported in dpkg without +relying on maintainer scripts at its sole discretion (examples +include rewriting B<rm_conffile> into dpkg's B<remove-on-upgrade>). +The minimum requirement for activating this feature is that debhelper +runs in compat 10 or later. + +Supports substitution variables in compat 13 and later as +documented in L<debhelper(7)>. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-D>I<TOKEN=VALUE>, B<--define> I<TOKEN=VALUE> + +Define tokens to be replaced inside the maintainer scripts when +it is generated. Please note that the limitations described in +L</Limitations in token names> also applies to tokens defined +on the command line. Invalid token names will trigger an error. + +In the simple case, this parameter will cause B<< #I<TOKEN># >> +to be replaced by I<VALUE>. If I<VALUE> starts with a literal +I<@>-sign, then I<VALUE> is expected to point to a file +containing the actual value to insert. + +An explicit declared token with this parameter will replace built-in +tokens. + +Test examples to aid with the understanding: + + cat >> debian/postinst <<EOF + #SIMPLE# + #FILEBASED# + EOF + echo -n "Complex value" > some-file + dh_installdeb --define SIMPLE=direct --define FILEBASED=@some-file + +In this example, B<#SIMPLE#> will expand to B<direct> and B<#FILEBASED#> +will expand to B<Complex value>. + +It is also possible to set package-specific values for a given +token. This is useful when B<dh_installdeb> is acting on multiple +packages that need different values for the same token. This is +done by prefixing the token name with B<< pkg.I<package-name>. >>. + +This can be used as in the following example: + + cat >> debian/foo.postinst <<EOF + # Script for #PACKAGE# + #TOKEN# + EOF + cat >> debian/bar.postinst <<EOF + # Script for #PACKAGE# + #TOKEN# + EOF + cat >> debian/baz.postinst <<EOF + # Script for #PACKAGE# + #TOKEN# + EOF + dh_installdeb -pfoo -pbar -pbaz --define TOKEN=default --define pkg.bar.TOKEN=unique-bar-value \ + --define pkg.baz.TOKEN=unique-baz-value + +In this example, B<#TOKEN#> will expand to B<default> in F<debian/foo.postinst>, +to B<unique-bar-value> in F<debian/bar.postinst> and to B<unique-baz-value> +in F<debian/baz.postinst>. + +Note that the B<#pkg.*#> tokens will be visible in all scripts acted on. E.g. +you can refer to B<#pkg.bar.TOKEN#> inside F<debian/foo.postinst> and it will +be replaced by B<unique-bar-value>. + +=back + +=head1 SUBSTITUTION IN MAINTAINER SCRIPTS + +The B<dh_installdeb> will automatically replace the following tokens +inside a provided maintainer script (if not replaced via B<-D>/B<--define>): + +=over 4 + +=item #DEBHELPER# + +This token is by default replaced with generated shell snippets debhelper +commands. This includes the snippets generated by +B<dh_installdeb> from I<package>.maintscript file (if present). + +=item #DEB_HOST_I<NAME>#, #DEB_BUILD_I<NAME>#, #DEB_TARGET_I<NAME># + +These tokens are replaced with the respective variable from +L<dpkg-architecture(1)>. In almost all cases, you will want +use the B<< #DEB_HOST_I<NAME> >> variant in a script to ensure +you get the right value when cross-building. + +On a best effort, tokens of this pattern that do not match +a variable in L<dpkg-architecture(1)> will be left as-is. + +=item #ENV.I<NAME># + +These tokens of this form will be replaced with value of the +corresponding environment variable. If the environment +variable is unset, the token is replaced with the empty +string. + +Note that there are limits on which names can be used (see +L</Limitations in token names>). + +=item #PACKAGE# + +This token is by default replaced by the package name, which will contain +the concrete script. + +=back + +=head2 Limitations in token names + +All tokens intended to be substituted must match the regex: #[A-Za-z0-9_.+]+# + +Tokens that do not match that regex will be silently ignored if found in the +script template. Invalid token names passed to B<-D> or B<--define> will +cause B<dh_installdeb> to reject the command with an error in most cases. + +=cut + +my %PROVIDED_SUBST; + +init(options => { + 'define|D=s%' => \%PROVIDED_SUBST, +}); + +# dpkg-maintscript-helper commands with their associated dpkg pre-dependency +# versions. +my %maintscript_predeps = ( + "rm_conffile" => "", + "mv_conffile" => "", + "symlink_to_dir" => "", + "dir_to_symlink" => "", +); +my %maintscript_validator = ( + "rm_conffile" => \&_validate_conffile_args, + "mv_conffile" => \&_validate_conffile_args, +); + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + + install_dir("$tmp/DEBIAN"); + + if (is_udeb($package)) { + # For udebs, only do the postinst, and no #DEBHELPER#. + # Udebs also support menutest and isinstallable scripts. + foreach my $script (qw{postinst menutest isinstallable}) { + my $f=pkgfile($package,$script); + if ($f) { + install_prog($f, "$tmp/DEBIAN/$script"); + } + } + + # stop here for udebs + next; + } + + my $maintscriptfile=pkgfile($package, "maintscript"); + my @special_conffiles_entries; + if ($maintscriptfile) { + if (compat(9)) { + foreach my $line (filedoublearray($maintscriptfile)) { + my $cmd=$line->[0]; + error("unknown dpkg-maintscript-helper command: $cmd") + unless exists $maintscript_predeps{$cmd}; + addsubstvar($package, "misc:Pre-Depends", "dpkg", + ">= $maintscript_predeps{$cmd}") + if length $maintscript_predeps{$cmd}; + my $params=escape_shell(@$line); + foreach my $script (qw{postinst preinst prerm postrm}) { + autoscript($package, $script, "maintscript-helper", + "s!#PARAMS#!$params!g"); + } + } + } else { + my @maintscripts = filedoublearray($maintscriptfile); + my @params; + foreach my $line (@maintscripts) { + my $cmd=$line->[0]; + error("unknown dpkg-maintscript-helper command: $cmd") + unless exists $maintscript_predeps{$cmd}; + addsubstvar($package, "misc:Pre-Depends", "dpkg", + ">= $maintscript_predeps{$cmd}") + if length $maintscript_predeps{$cmd}; + if (my $validator = $maintscript_validator{$cmd}) { + $validator->($package, @{$line}); + } + if (0) { # Disabled for now: #994919 + #994903 + my $current_conffile = $line->[1]; + push(@special_conffiles_entries, "remove-on-upgrade ${current_conffile}"); + addsubstvar($package, "misc:Pre-Depends", "dpkg (>= 1.20.6~)"); + next; + } + push(@params, escape_shell(@{$line}) ); + } + if (@params) { + foreach my $script (qw{postinst preinst prerm postrm}) { + my $subst = sub { + my @res; + chomp; + for my $param (@params) { + my $line = $_; + $line =~ s{#PARAMS#}{$param}g; + push(@res, $line); + } + $_ = join("\n", @res) . "\n"; + }; + autoscript($package, $script, "maintscript-helper", $subst); + } + } + } + } + + # Install debian scripts. + my $package_subst = debhelper_script_per_package_subst($package, \%PROVIDED_SUBST); + foreach my $script (qw{postinst preinst prerm postrm}) { + debhelper_script_subst($package, $script, $package_subst); + } + + # Install non-executable files + my @non_exec_files; + # Removed in compat 12. + push(@non_exec_files, 'conffiles'); + # In compat 10, we let dh_makeshlibs handle "shlibs". + push(@non_exec_files, 'shlibs') if compat(9); + foreach my $file (@non_exec_files) { + my $f=pkgfile($package,$file); + if ($f) { + install_file($f, "$tmp/DEBIAN/$file"); + } + } + + install_triggers($package, $tmp); + if (@special_conffiles_entries) { + open(my $fd, '>>', "$tmp/DEBIAN/conffiles"); + for my $line (@special_conffiles_entries) { + print {$fd} "${line}\n"; + } + close($fd); + } + + # Automatic conffiles registration: If it is in /etc, it is a + # conffile. + if ( -d "$tmp/etc") { + complex_doit("find $tmp/etc -type f -printf '/etc/%P\n' | LC_ALL=C sort >> $tmp/DEBIAN/conffiles"); + # Anything found? + if (-z "$tmp/DEBIAN/conffiles") { + rm_files("$tmp/DEBIAN/conffiles"); + } + } + + if ( -f "$tmp/DEBIAN/conffiles") { + reset_perm_and_owner(0644, "$tmp/DEBIAN/conffiles"); + } +} + +sub install_triggers { + my ($package, $tmp) = @_; + my $generated = generated_file($package, 'triggers', 0); + my @sources = grep { -f $_ } ( + pkgfile($package, 'triggers'), + $generated, + ); + my $target = "$tmp/DEBIAN/triggers"; + return if not @sources; + if (@sources > 1) { + my $merged = "${generated}.merged"; + open(my $ofd, '>', $merged) + or error("open ${target} failed: $!"); + for my $src (@sources) { + open(my $ifd, '<', $src) + or error("open ${src} failed: $!"); + print {$ofd} $_ while <$ifd>; + close($ifd); + } + close($ofd) or error("close ${merged} failed: $!"); + @sources = ($merged); + } + install_file($sources[0], $target); +} + +sub _validate_conffile_args { + my ($package, $cmd, @args) = @_; + my ($current_conffile, $new_conffile, $prior_version, $owning_package, $other); + for my $arg (@args) { + if ($arg eq '--') { + _maybe_error("The maintscripts file for $package includes a \"--\" for one of the ${cmd} commands, but it should not"); + } + } + if ($cmd eq 'rm_conffile') { + ($current_conffile, $prior_version, $owning_package, $other) = @args; + } else { + ($current_conffile, $new_conffile, $prior_version, $owning_package, $other) = @args; + } + $current_conffile //= ''; + _maybe_error("The current conffile path for ${cmd} must be present and absolute, got ${current_conffile}") + if not $current_conffile or substr($current_conffile, 0, 1) ne '/'; + _maybe_error("The new conffile path for ${cmd} must be present and absolute, got ${new_conffile}") + if $cmd eq 'mv_conffile' and (not $new_conffile or substr($new_conffile, 0, 1) ne '/'); + + _maybe_error("The version for ${cmd} ${current_conffile} is not valid, got ${prior_version}") + if $prior_version and $prior_version !~ m{^${Debian::Debhelper::Dh_Lib::PKGVERSION_REGEX}$}o; + _maybe_error("The owning package for ${cmd} ${current_conffile} is not valid, got ${owning_package}") + if $owning_package and $owning_package !~ m{^${Debian::Debhelper::Dh_Lib::PKGNAME_REGEX}$}o; + if (defined($other)) { + warning("Too many arguments for ${cmd} ${current_conffile}"); + } +} + +sub _maybe_error { + my ($msg) = @_; + if (compat(11)) { + warning($msg); + } else { + error($msg); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installdebconf b/dh_installdebconf new file mode 100755 index 0000000..79d50d2 --- /dev/null +++ b/dh_installdebconf @@ -0,0 +1,243 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installdebconf - install files used by debconf in package build directories + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installdebconf> [S<I<debhelper options>>] [B<-n>] [S<B<--> I<params>>] + +=head1 DESCRIPTION + +B<dh_installdebconf> is a debhelper program that is responsible for installing +files used by debconf into package build directories. + +It also automatically generates the F<postrm> commands needed to interface +with debconf. The commands are added to the maintainer scripts by +B<dh_installdeb>. See L<dh_installdeb(1)> for an explanation of how that +works. + +Note that if you use debconf, your package probably needs to depend on it +(it will be added to B<${misc:Depends}> by this program). + +Note that for your config script to be called by B<dpkg>, your F<postinst> +needs to source debconf's confmodule. B<dh_installdebconf> does not +install this statement into the F<postinst> automatically as it is too +hard to do it right. + +=head1 FILES + +=over 4 + +=item debian/I<package>.config + +This is the debconf F<config> script, and is installed into the F<DEBIAN> +directory in the package build directory. + +Inside the script, the token B<#DEBHELPER#> is replaced with +shell script snippets generated by other debhelper commands. + +=item debian/I<package>.templates + +This is the debconf F<templates> file, and is installed into the F<DEBIAN> +directory in the package build directory. + +=item F<debian/po/> + +If this directory is present, this program will automatically use +L<po2debconf(1)> to generate merged templates +files that include the translations from there. + +For this to work, your package should build-depend on F<po-debconf>. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-n>, B<--no-scripts> + +Do not modify F<postrm> script. + +=item B<--> I<params> + +Pass the params to B<po2debconf>. + +=item B<-D>I<TOKEN=VALUE>, B<--define> I<TOKEN=VALUE> + +Define tokens to be replaced inside the maintainer scripts when +it is generated. Please note that the limitations described in +L</Limitations in token names> also applies to tokens defined +on the command line. Invalid token names will trigger an error. + +In the simple case, this parameter will cause B<< #I<TOKEN># >> +to be replaced by I<VALUE>. If I<VALUE> starts with a literal +I<@>-sign, then I<VALUE> is expected to point to a file +containing the actual value to insert. + +An explicit declared token with this parameter will replace built-in +tokens. + +Test examples to aid with the understanding: + + cat >> debian/config <<EOF + #SIMPLE# + #FILEBASED# + EOF + echo -n "Complex value" > some-file + dh_installdeb --define SIMPLE=direct --define FILEBASED=@some-file + +In this example, B<#SIMPLE#> will expand to B<direct> and B<#FILEBASED#> +will expand to B<Complex value>. + +It is also possible to set package-specific values for a given +token. This is useful when B<dh_installdebconf> is acting on multiple +packages that need different values for the same token. This is +done by prefixing the token name with B<< pkg.I<package-name>. >>. + +This can be used as in the following example: + + cat >> debian/foo.config <<EOF + # Script for #PACKAGE# + #TOKEN# + EOF + cat >> debian/bar.config <<EOF + # Script for #PACKAGE# + #TOKEN# + EOF + cat >> debian/baz.config <<EOF + # Script for #PACKAGE# + #TOKEN# + EOF + dh_installdebconf -pfoo -pbar -pbaz --define TOKEN=default --define pkg.bar.TOKEN=unique-bar-value \ + --define pkg.baz.TOKEN=unique-baz-value + +In this example, B<#TOKEN#> will expand to B<default> in F<debian/foo.config>, +to B<unique-bar-value> in F<debian/bar.config> and to B<unique-baz-value> +in F<debian/baz.config>. + +Note that the B<#pkg.*#> tokens will be visible in all scripts acted on. E.g. +you can refer to B<#pkg.bar.TOKEN#> inside F<debian/foo.config> and it will +be replaced by B<unique-bar-value>. + +=back + +=head1 SUBSTITUTION IN MAINTAINER SCRIPTS + +The B<dh_installdebconf> will automatically replace the following tokens +inside a provided maintainer script (if not replaced via B<-D>/B<--define>): + +=over 4 + +=item #DEB_HOST_I<NAME>#, #DEB_BUILD_I<NAME>#, #DEB_TARGET_I<NAME># + +These tokens are replaced with the respective variable from +L<dpkg-architecture(1)>. In almost all cases, you will want +use the B<< #DEB_HOST_I<NAME> >> variant in a script to ensure +you get the right value when cross-building. + +On a best effort, tokens of this pattern that do not match +a variable in L<dpkg-architecture(1)> will be left as-is. + +=item #ENV.I<NAME># + +These tokens of this form will be replaced with value of the +corresponding environment variable. If the environment +variable is unset, the token is replaced with the empty +string. + +Note that there are limits on which names can be used (see +L</Limitations in token names>). + +=item #PACKAGE# + +This token is by default replaced by the package name, which will contain +the concrete script. + +=back + +=head2 Limitations in token names + +All tokens intended to be substituted must match the regex: #[A-Za-z0-9_.+]+# + +Tokens that do not match that regex will be silently ignored if found in the +script template. Invalid token names passed to B<-D> or B<--define> will +cause B<dh_installdebconf> to reject the command with an error in most cases. + +=cut + +my %PROVIDED_SUBST; + +init(options => { + 'define|D=s%' => \%PROVIDED_SUBST, +}); + +my @extraparams; +if (defined($dh{U_PARAMS})) { + @extraparams=@{$dh{U_PARAMS}}; +} + +# PROMISE: DH NOOP WITHOUT config templates cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + my $config=pkgfile($package,"config"); + my $templates=pkgfile($package,"templates"); + + install_dir("$tmp/DEBIAN"); + + if (! is_udeb($package)) { + # Install debian scripts. + my $package_subst = debhelper_script_per_package_subst($package, \%PROVIDED_SUBST); + debhelper_script_subst($package, "config", $package_subst); + } + + if ($templates ne '') { + # Are there old-style translated templates? + if (glob("$templates.??"), glob("$templates.??_??")) { + warning "Ignoring debian/templates.ll files. Switch to po-debconf!"; + } + + umask(0022); # since I do a redirect below + + if (-d "debian/po") { + complex_doit("po2debconf @extraparams $templates > $tmp/DEBIAN/templates"); + } + else { + install_file($templates,"$tmp/DEBIAN/templates"); + } + } + + # I'm going with debconf 0.5 because it was the first + # "modern" one. udebs just need cdebconf. + my $debconfdep=is_udeb($package) ? "cdebconf-udeb" : "debconf (>= 0.5) | debconf-2.0"; + if ($config ne '' || $templates ne '') { + addsubstvar($package, "misc:Depends", $debconfdep); + } + + if (($config ne '' || $templates ne '') && ! $dh{NOSCRIPTS}) { + autoscript($package,"postrm","postrm-debconf"); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installdirs b/dh_installdirs new file mode 100755 index 0000000..8d33b10 --- /dev/null +++ b/dh_installdirs @@ -0,0 +1,141 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installdirs - create subdirectories in package build directories + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installdirs> [S<I<debhelper options>>] [B<-A>] [B<--sourcedir=>I<dir>] [B<--create-in-sourcedir>] [S<I<dir> ...>] + +=head1 DESCRIPTION + +B<dh_installdirs> is a debhelper program that is responsible for creating +subdirectories in package build directories. + +Many packages can get away with omitting the call to B<dh_installdirs> +completely. Notably, other B<dh_*> commands are expected to create +directories as needed. + +=head1 FILES + +=over 4 + +=item debian/I<package>.dirs + +Lists directories to be created in I<package>. + +Generally, there is no need to list directories created by the +upstream build system or directories needed by other B<debhelper> +commands. + +Supports substitution variables in compat 13 and later as +documented in L<debhelper(7)>. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-A>, B<--all> + +Create any directories specified by command line parameters in ALL packages +acted on, not just the first. + +=item B<--create-in-sourcedir>, B<--no-create-in-sourcedir> + +Whether to create the specified directories in the source directory +(usually F<debian/tmp>) I<in addition to> in the package build directory +(usually F<< debian/I<package> >>). + +The default is B<--no-create-in-sourcedir>. + +=item B<--sourcedir=>I<dir> + +Consider I<dir> the source directory for the packages acted on instead +of the default (which is usually F<debian/tmp>). + +Please note that this option is dependent on the +B<--create-in-sourcedir> option (when B<--no-create-in-sourcedir> is +in effect, this option does nothing in B<dh_installdirs>). + +=item I<dir> ... + +Create these directories in the package build directory of the first +package acted on. (Or in all packages if B<-A> is specified.) + +=back + +=cut + +my $create_in_sourcedir = 0; + +init(options => { + 'sourcedir=s' => \$dh{SOURCEDIR}, + 'create-in-sourcedir!' => \$create_in_sourcedir, +}); + +# PROMISE: DH NOOP WITHOUT dirs cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + my $file=pkgfile($package,"dirs"); + my $srcdir = $dh{SOURCEDIR} // default_sourcedir($package); + + install_dir($tmp) if compat(10); + + my @dirs; + + if ($file) { + @dirs=filearray($file) + } + + if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) { + push @dirs, @ARGV; + } + + if (@dirs) { + # Stick the $tmp onto the front of all the dirs. + # This is necessary, for 2 reasons, one to make them + # be in the right directory, but more importantly, it + # protects against the danger of absolute dirs being + # specified. + my @make_dirs; + push(@make_dirs, map { + my $dir = "$tmp/$_"; + $dir =~ tr:/:/:s; # just beautification. + $dir; + } @dirs); + if ($create_in_sourcedir) { + push(@make_dirs, map { + my $dir = "${srcdir}/$_"; + $dir =~ tr:/:/:s; # just beautification. + $dir; + } @dirs); + } + + # Create dirs. + install_dir(@make_dirs); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installdocs b/dh_installdocs new file mode 100755 index 0000000..2e4529d --- /dev/null +++ b/dh_installdocs @@ -0,0 +1,447 @@ +#!/usr/bin/perl + +=encoding UTF-8 + +=head1 NAME + +dh_installdocs - install documentation into package build directories + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installdocs> [S<I<debhelper options>>] [B<-A>] [B<-X>I<item>] [S<I<file> ...>] + +=head1 DESCRIPTION + +B<dh_installdocs> is a debhelper program that is responsible for installing +documentation into F<usr/share/doc/package> in package build directories. + +In compat 10 and earlier, L<dh_install(1)> may be a better tool for handling +the upstream documentation, when upstream's own build system installs all the desired documentation +correctly. In this case, B<dh_installdocs> is still useful for installing +packaging related documentation (e.g. the F<debian/copyright> file). + +From debhelper compatibility level 11 on, B<dh_install> will fall back to +looking in F<debian/tmp> for files, if it does not find them in the current +directory (or wherever you've told it to look using B<--sourcedir>). + +In compat 11 and later, B<dh_installdocs> offers many of the features that +L<dh_install(1)> also has. Furthermore, B<dh_installdocs> also supports +the B<nodoc> build profile to exclude documentation (regardless of compat +level). + +=head1 FILES + +=over 4 + +=item debian/I<package>.docs + +List documentation files to be installed into I<package>. + +Supports substitution variables in compat 13 and later as +documented in L<debhelper(7)>. + +=item F<debian/copyright> + +The copyright file is installed into all packages, unless a more +specific copyright file is available. + +=item debian/I<package>.copyright + +=item debian/I<package>.README.Debian + +=item debian/I<package>.TODO + +Each of these files is automatically installed if present for a +I<package>. + +=item F<debian/README.Debian> + +=item F<debian/TODO> + +These files are installed into the first binary package listed in +debian/control. + +Note that F<README.debian> files are also installed as F<README.Debian>, +and F<TODO> files will be installed as F<TODO.Debian> in non-native packages. + +=item debian/I<package>.doc-base + +Installed as doc-base control files. Note that the doc-id will be +determined from the B<Document:> entry in the doc-base control file in +question. In the event that multiple doc-base files in a single source +package share the same doc-id, they will be installed to +usr/share/doc-base/package instead of usr/share/doc-base/doc-id. + +=item debian/I<package>.doc-base.* + +If your package needs to register more than one document, you need +multiple doc-base files, and can name them like this. In the event +that multiple doc-base files of this style in a single source package +share the same doc-id, they will be installed to +usr/share/doc-base/package-* instead of usr/share/doc-base/doc-id. + +Please be aware that this deduplication is currently done in memory +only, so for now it requires B<dh_installdocs> to be called no more +than once during the package build. Calling B<dh_installdocs +-p>I<package> in combination with using +F<debian/>I<package>F<.doc-base.*> files can lead to uninstallable +packages. See L<https://bugs.debian.org/980903> for details. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-A>, B<--all> + +Install all files specified by command line parameters in ALL packages +acted on. + +=item B<-X>I<item>, B<--exclude=>I<item> + +Exclude files that contain I<item> anywhere in their filename from +being installed. Note that this includes doc-base files. + +=item B<--sourcedir=>I<dir> + +Look in the specified directory for files to be installed. This option +requires compat 11 or later (it is silently ignored in compat 10 or earlier). + +Note that this is not the same as the B<--sourcedirectory> option used +by the B<dh_auto_>I<*> commands. You rarely need to use this option, since +B<dh_installman> automatically looks for files in F<debian/tmp> in debhelper +compatibility level 11 and above. + +=item B<--doc-main-package=>I<main-package> + +Set the main package for a documentation package. This is used to +install the documentation of the documentation package in F<< +/usr/share/doc/I<main-package> >> as recommended by the Debian policy +manual 3.9.7 in §12.3. + +In compat 11 (or later), this option is only useful if debhelper's +auto-detection of the main package is wrong. The option can also be +used to silence a warning from debhelper when the auto-detection fails +but the default happens to be correct. + +This option cannot be used when B<dh_installdocs> is instructed to act +on multiple packages. If you need this option, you will generally +need to combine it with B<-p> to ensure exactly one package is acted +on. + +Please keep in mind that some documentation (the copyright file, +README.Debian, etc.) will be unaffected by this option. + +=item B<--link-doc=>I<package> + +Make the documentation directory of all packages acted on be a symlink to +the documentation directory of I<package>. This has no effect when acting on +I<package> itself, or if the documentation directory to be created already +exists when B<dh_installdocs> is run. To comply with policy, I<package> must +be a binary package that comes from the same source package. + +debhelper will try to avoid installing files into linked documentation +directories that would cause conflicts with the linked package. The B<-A> +option will have no effect on packages with linked documentation +directories, and F<copyright>, F<changelog>, F<README.Debian>, and F<TODO> files will +not be installed. + +(An older method to accomplish the same thing, which is still supported, +is to make the documentation directory of a package be a dangling symlink, +before calling B<dh_installdocs>.) + +Please note that this option only applies to the documentation +directory for the package itself. When the package ships +documentation for another package (e.g. see B<--doc-main-package>), it +will not use a symlink for the documentation of the other package. + + +B<CAVEAT 1>: If a previous version of the package was built without this +option and is now built with it (or vice-versa), it requires a "dir to +symlink" (or "symlink to dir") migration. Since debhelper has no +knowledge of previous versions, you have to enable this migration +itself. + +This can be done by providing a "debian/I<package>.maintscript" file +and using L<dh_installdeb(1)> to provide the relevant maintainer +script snippets. + +B<CAVEAT 2>: The use of B<--link-doc> should only be done when the +packages have same "architecture" type. A link from an architecture +independent package to an architecture dependent package (or vice +versa) will not work. Since compat 10, debhelper will actively reject +unsupported combinations. + +=item I<file> ... + +Install these files as documentation into the first package acted on. (Or +in all packages if B<-A> is specified). + +=back + +=head1 EXAMPLES + +This is an example of a F<debian/package.docs> file: + + README + TODO + debian/notes-for-maintainers.txt + docs/manual.txt + docs/manual.pdf + docs/manual-html/ + +=head1 NOTES + +Note that B<dh_installdocs> will happily copy entire directory hierarchies if +you ask it to (similar to B<cp -a>). If it is asked to install a +directory, it will install the complete contents of the directory. + +=cut + +my %docdir_created; +# Create documentation directories on demand. This allows us to use dangling +# symlinks for linked documentation directories unless additional files need +# to be installed. +sub ensure_docdir { + my $package=shift; + return if $docdir_created{$package}; + my $tmp=tmpdir($package); + + my $target; + if ($dh{LINK_DOC} && $dh{LINK_DOC} ne $package) { + $target="$tmp/usr/share/doc/$dh{LINK_DOC}"; + } + else { + $target="$tmp/usr/share/doc/$package"; + } + + # If this is a symlink, leave it alone. + if (! -d $target && ! -l $target) { + install_dir($target); + } + $docdir_created{$package}=1; +} + +init(options => { + "link-doc=s" => \$dh{LINK_DOC}, + "sourcedir=s" => \$dh{SOURCEDIR}, + 'doc-main-package=s' => \$dh{DOC_MAIN_PACKAGE}, +}); + +my $called_getpackages = 0; +my $default_error_handler = compat(10) ? \&glob_expand_error_handler_reject_nomagic_warn_discard : \&glob_expand_error_handler_reject; +my $nodocs = is_build_profile_active('nodoc') || get_buildoption('nodoc') ? 1 : 0; +# We cannot assume documentation is built under nodoc, but if it is we must flag it as handled +# or dh_missing might make noise. +$default_error_handler = \&glob_expand_error_handler_silently_ignore if $nodocs; + +if (@{$dh{DOPACKAGES}} > 1 and $dh{DOC_MAIN_PACKAGE}) { + error('--doc-main-package should be used with -p<doc-pkg>'); +} + +if ($dh{DOC_MAIN_PACKAGE}) { + assert_opt_is_known_package($dh{DOC_MAIN_PACKAGE}, '--doc-main-package'); +} + +foreach my $package (getpackages()) { + next if is_udeb($package); + + my $tmp=tmpdir($package); + my $file=pkgfile($package,"docs"); + my $link_doc=($dh{LINK_DOC} && $dh{LINK_DOC} ne $package); + my $skip_install = process_pkg($package) ? 0 : 1; + my @search_dirs = ('.'); + my $error_handler = $skip_install ? \&glob_expand_error_handler_silently_ignore : $default_error_handler; + @search_dirs = ($dh{SOURCEDIR} // '.', default_sourcedir($package)) if not compat(10); + + if (not $skip_install) { + if ($link_doc) { + getpackages('both') unless $called_getpackages++; + + if (package_binary_arch($package) ne package_binary_arch($dh{LINK_DOC})) { + if (compat(9)) { + warning("WARNING: --link-doc between architecture all and not all packages breaks binNMUs"); + } else { + error("--link-doc not allowed between ${package} and $dh{LINK_DOC} (one is arch:all and the other not)"); + } + } + # Make sure that the parent directory exists. + if (!-d "$tmp/usr/share/doc" && !-l "$tmp/usr/share/doc") { + install_dir("$tmp/usr/share/doc"); + } + # Create symlink to another documentation directory if + # necessary. + if (!-d "$tmp/usr/share/doc/$package" && + !-l "$tmp/usr/share/doc/$package") { + make_symlink_raw_target($dh{LINK_DOC}, "$tmp/usr/share/doc/$package"); + # Policy says that if you make your documentation + # directory a symlink, then you have to depend on + # the target. + addsubstvar($package, 'misc:Depends', "$dh{LINK_DOC} (= \${binary:Version})"); + } + } else { + ensure_docdir($package); + } + } + + my @docs; + + if ($file) { + @docs = filearray($file, \@search_dirs, $error_handler); + } + + if (($package eq $dh{FIRSTPACKAGE} || ($dh{PARAMS_ALL} && !$link_doc)) && @ARGV) { + push @docs, @ARGV; + } + + log_installed_files($package, @docs); + + next if $skip_install; + + if (not $nodocs and @docs) { + my $exclude = ' -and ! -empty'; + my $target_package = compute_doc_main_package($package); + if (not defined($target_package)) { + warning("Cannot auto-detect main package for ${package}. If the default is wrong, please use --doc-main-package"); + $target_package = $package; + } elsif ($dh{PARAMS_ALL} and $package ne $target_package and not $dh{DOC_MAIN_PACKAGE}) { + warning("Not using auto-detected $target_package as main doc package for $package: With -A/--all, this would cause file-conflicts."); + $target_package = $package; + } + if ($dh{EXCLUDE_FIND}) { + $exclude .= ' -and ! \( '.$dh{EXCLUDE_FIND}.' \)'; + } + my $target_dir = "${tmp}/usr/share/doc/${target_package}"; + install_dir($target_dir) unless -l $target_dir; + + foreach my $doc (@docs) { + next if excludefile($doc); + next if -f $doc && ! -s _; # ignore empty files + ensure_docdir($package); + if (-d $doc && length $exclude) { + my $basename = basename($doc); + my $dir = ($basename eq '.') ? $doc : "$doc/.."; + my $pwd=`pwd`; + chomp $pwd; + # Gracefully handling tmpdir being absolute (-P/...) + my $docdir = $target_dir =~ m{^/} ? $target_dir : "${pwd}/${target_dir}"; + complex_doit("cd '$dir' && " . + "find '$basename' \\( -type f -or -type l \\)$exclude -print0 | LC_ALL=C sort -z | " . + "xargs -0 -I {} cp --reflink=auto --parents -dp {} $docdir"); + } + else { + doit("cp", '--reflink=auto', "-a", $doc, $target_dir); + } + } + doit("chown","-R","0:0","$tmp/usr/share/doc") if should_use_root(); + doit("chmod","-R","u+rw,go=rX","$tmp/usr/share/doc"); + } + + # .Debian is correct, according to policy, but I'm easy. + my $readme_debian=pkgfile($package,'README.Debian'); + if (! $readme_debian) { + $readme_debian=pkgfile($package,'README.debian'); + } + if (! $link_doc && $readme_debian && ! excludefile($readme_debian)) { + ensure_docdir($package); + install_file($readme_debian, + "$tmp/usr/share/doc/$package/README.Debian"); + } + + my $todo=pkgfile($package,'TODO'); + if (! $link_doc && $todo && ! excludefile($todo)) { + ensure_docdir($package); + if (isnative($package)) { + install_file($todo, "$tmp/usr/share/doc/$package/TODO"); + } + else { + install_file($todo, + "$tmp/usr/share/doc/$package/TODO.Debian"); + } + } + + # If the "directory" is a dangling symlink, then don't install + # the copyright file. This is useful for multibinary packages + # that share a doc directory. + if (! $link_doc && (! -l "$tmp/usr/share/doc/$package" || -d "$tmp/usr/share/doc/$package")) { + # Support debian/package.copyright, but if not present, fall + # back on debian/copyright for all packages, not just the + # main binary package. + my $copyright=pkgfile($package,'copyright'); + if (! $copyright && -e "debian/copyright") { + $copyright="debian/copyright"; + } + if ($copyright && ! excludefile($copyright)) { + ensure_docdir($package); + install_file($copyright, + "$tmp/usr/share/doc/$package/copyright"); + } + } + + next if $nodocs; + + # Handle doc-base files. There are two filename formats, the usual + # plus an extended format (debian/package.*). + my %doc_ids; + + opendir(DEB,"debian/") || error("can't read debian directory: $!"); + # If this is the main package, we need to handle unprefixed filenames. + # For all packages, we must support both the usual filename format plus + # that format with a period an something appended. + my $regexp="\Q$package\E\."; + if ($package eq $dh{MAINPACKAGE}) { + $regexp="(|$regexp)"; + } + foreach my $fn (grep {/^${regexp}doc-base(\..*)?$/} readdir(DEB)) { + # .EX are example files, generated by eg, dh-make + next if $fn=~/\.EX$/; + next if excludefile($fn); + # Parse the file to get the doc id. + open(my $fd, '<', "debian/$fn") || die "Cannot read debian/$fn."; + while (<$fd>) { + s/\s*$//; + if (/^Document\s*:\s*(.*)/) { + $doc_ids{$fn}=$1; + last; + } + } + if (! exists $doc_ids{$fn}) { + warning("Could not parse $fn for doc-base Document id; skipping"); + } + close($fd); + } + closedir(DEB); + + if (%doc_ids) { + install_dir("$tmp/usr/share/doc-base/"); + } + foreach my $fn (keys %doc_ids) { + # To avoid issues with duplicated document IDs, we will always + # install to usr/share/doc-base/<packagename>.<doc_id> instead + # of just usr/share/doc-base/<packagename> or just + # usr/share/doc-base/<doc_id>. See #525821 and #980903. + install_file("debian/$fn", + "$tmp/usr/share/doc-base/$package.$doc_ids{$fn}"); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installemacsen b/dh_installemacsen new file mode 100755 index 0000000..bae900a --- /dev/null +++ b/dh_installemacsen @@ -0,0 +1,149 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installemacsen - register an Emacs add on package + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installemacsen> [S<I<debhelper options>>] [B<-n>] [B<--priority=>I<n>] [B<--flavor=>I<foo>] + +=head1 DESCRIPTION + +B<dh_installemacsen> is a debhelper program that is responsible for installing +files used by the Debian B<emacsen-common> package into package build +directories. + +It also automatically generates the F<preinst> F<postinst> and F<prerm> +commands needed to register a package as an Emacs add on package. The commands +are added to the maintainer scripts by B<dh_installdeb>. See +L<dh_installdeb(1)> for an explanation of how this works. + +=head1 FILES + +=over 4 + +=item debian/I<package>.emacsen-compat + +Installed into F<usr/lib/emacsen-common/packages/compat/package> in the +package build directory. + +=item debian/I<package>.emacsen-install + +Installed into F<usr/lib/emacsen-common/packages/install/package> in the +package build directory. + +=item debian/I<package>.emacsen-remove + +Installed into F<usr/lib/emacsen-common/packages/remove/package> in the +package build directory. + +=item debian/I<package>.emacsen-startup + +Installed into etc/emacs/site-start.d/50I<package>.el in the package +build directory. Use B<--priority> to use a different priority than 50. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-n>, B<--no-scripts> + +Do not modify F<postinst>/F<prerm> scripts. + +=item B<--priority=>I<n> + +Sets the priority number of a F<site-start.d> file. Default is 50. + +=item B<--flavor=>I<foo> + +Sets the flavor a F<site-start.d> file will be installed in. Default is +B<emacs>, alternatives include B<xemacs> and B<emacs20>. + +=back + +=head1 NOTES + +Note that this command is not idempotent. L<dh_prep(1)> should be called +between invocations of this command. Otherwise, it may cause multiple +instances of the same text to be added to maintainer scripts. + +=cut + +init(options => { + "flavor=s" => \$dh{FLAVOR}, + "priority=s" => \$dh{PRIORITY}, +}); + +if (! defined $dh{PRIORITY}) { + $dh{PRIORITY}=50; +} +if (! defined $dh{FLAVOR}) { + $dh{FLAVOR}='emacs'; +} + +# PROMISE: DH NOOP WITHOUT emacsen-common emacsen-install emacsen-remove emacsen-startup cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + + my $emacsen_compat=pkgfile($package,"emacsen-compat"); + my $emacsen_install=pkgfile($package,"emacsen-install"); + my $emacsen_remove=pkgfile($package,"emacsen-remove"); + my $emacsen_startup=pkgfile($package,"emacsen-startup"); + + if ($emacsen_compat ne '') { + install_dir("$tmp/usr/lib/emacsen-common/packages/compat"); + install_file($emacsen_compat, + "$tmp/usr/lib/emacsen-common/packages/compat/$package"); + } + + if ($emacsen_install ne '') { + install_dir("$tmp/usr/lib/emacsen-common/packages/install"); + install_prog($emacsen_install,"$tmp/usr/lib/emacsen-common/packages/install/$package"); + } + + if ($emacsen_remove ne '') { + install_dir("$tmp/usr/lib/emacsen-common/packages/remove"); + install_prog("$emacsen_remove","$tmp/usr/lib/emacsen-common/packages/remove/$package"); + } + + if ($emacsen_startup ne '') { + install_dir("$tmp/etc/$dh{FLAVOR}/site-start.d/"); + install_file($emacsen_startup,"$tmp/etc/$dh{FLAVOR}/site-start.d/$dh{PRIORITY}$package.el"); + } + + if ($emacsen_install ne '' || $emacsen_remove ne '') { + if (! $dh{NOSCRIPTS}) { + autoscript($package,"preinst","preinst-emacsen", + { 'PACKAGE' => $package }); + autoscript($package,"postinst","postinst-emacsen", + { 'PACKAGE' => $package }); + autoscript($package,"prerm","prerm-emacsen", + { 'PACKAGE' => $package }); + } + } +} + +=head1 SEE ALSO + +L<debhelper(7)> +L</usr/share/doc/emacsen-common/debian-emacs-policy.gz> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installexamples b/dh_installexamples new file mode 100755 index 0000000..ce970ac --- /dev/null +++ b/dh_installexamples @@ -0,0 +1,192 @@ +#!/usr/bin/perl + +=encoding UTF-8 + +=head1 NAME + +dh_installexamples - install example files into package build directories + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installexamples> [S<I<debhelper options>>] [B<-A>] [B<-X>I<item>] [S<I<file> ...>] + +=head1 DESCRIPTION + +B<dh_installexamples> is a debhelper program that is responsible for +installing examples into F<usr/share/doc/package/examples> in package +build directories. + +From debhelper compatibility level 11 on, B<dh_install> will fall back to +looking in F<debian/tmp> for files, if it does not find them in the current +directory (or wherever you've told it to look using B<--sourcedir>). + +=head1 FILES + +=over 4 + +=item debian/I<package>.examples + +Lists example files or directories to be installed. + +If upstream provides an F<examples> directory, you will often want to use B<examples/*> rather +than B<examples> in this file. The latter would create +F<< /usr/share/doc/I<package>/examples/examples >>, which is rarely what you want. + +Supports substitution variables in compat 13 and later as +documented in L<debhelper(7)>. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-A>, B<--all> + +Install any files specified by command line parameters in ALL packages +acted on. + +=item B<--sourcedir=>I<dir> + +Look in the specified directory for files to be installed. This option +requires compat 11 or later (it is silently ignored in compat 10 or earlier). + +Note that this is not the same as the B<--sourcedirectory> option used +by the B<dh_auto_>I<*> commands. You rarely need to use this option, since +B<dh_installexamples> automatically looks for files in F<debian/tmp> in debhelper +compatibility level 11 and above. + +=item B<--doc-main-package=>I<main-package> + +Set the main package for a documentation package. This is used to +install the documentation of the documentation package in F<< +/usr/share/doc/I<main-package> >> as recommended by the Debian policy +manual 3.9.7 in §12.3. + +In compat 11 (or later), this option is only useful if debhelper's +auto-detection of the main package is wrong. The option can also be +used to silence a warning from debhelper when the auto-detection fails +but the default happens to be correct. + +This option cannot be used when B<dh_installexamples> is instructed to act +on multiple packages. If you need this option, you will generally +need to combine it with B<-p> to ensure exactly one package is acted +on. + +=item B<-X>I<item>, B<--exclude=>I<item> + +Exclude files that contain I<item> anywhere in their filename from +being installed. + +=item I<file> ... + +Install these files (or directories) as examples into the first package +acted on. (Or into all packages if B<-A> is specified.) + +=back + +=head1 NOTES + +Note that B<dh_installexamples> will happily copy entire directory hierarchies +if you ask it to (similar to B<cp -a>). If it is asked to install a +directory, it will install the complete contents of the directory. + +=cut + +init(options => { + "sourcedir=s" => \$dh{SOURCEDIR}, + 'doc-main-package=s' => \$dh{DOC_MAIN_PACKAGE}, +}); + +# PROMISE: DH NOOP WITHOUT pkgfile-logged(examples) cli-options() + +my $pwd; +my $default_error_handler = compat(10) ? \&glob_expand_error_handler_reject_nomagic_warn_discard : \&glob_expand_error_handler_reject; +my $nodocs = is_build_profile_active('nodoc') || get_buildoption('nodoc') ? 1 : 0; +# We cannot assume documentation is built under nodoc, but if it is we must flag it as handled +# or dh_missing might make noise. +$default_error_handler = \&glob_expand_error_handler_silently_ignore if $nodocs; + +if (@{$dh{DOPACKAGES}} > 1 and $dh{DOC_MAIN_PACKAGE}) { + error('--doc-main-package should be used with -p<doc-pkg>'); +} +if ($dh{DOC_MAIN_PACKAGE}) { + assert_opt_is_known_package($dh{DOC_MAIN_PACKAGE}, '--doc-main-package'); +} + +foreach my $package (getpackages()) { + next if is_udeb($package); + + my $tmp=tmpdir($package); + my $file=pkgfile($package,"examples"); + my @search_dirs = ('.'); + my $skip_install = process_pkg($package) ? 0 : 1; + my $error_handler = $skip_install ? \&glob_expand_error_handler_silently_ignore : $default_error_handler; + @search_dirs = ($dh{SOURCEDIR} // '.', default_sourcedir($package)) if not compat(10); + + my @examples; + + if ($file) { + @examples = filearray($file, \@search_dirs, $error_handler) if $file; + } + + if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) { + push @examples, @ARGV; + } + + log_installed_files($package, @examples); + + next if $skip_install or $nodocs; + + if (@examples) { + my $target_package = compute_doc_main_package($package); + if (not defined($target_package)) { + warning("Cannot auto-detect main package for ${package}. If the default is wrong, please use --doc-main-package"); + $target_package = $package; + } + my $target_dir = "${tmp}/usr/share/doc/${target_package}/examples"; + install_dir($target_dir); + + my $exclude = ''; + if ($dh{EXCLUDE_FIND}) { + $exclude .= ' -and ! \( '.$dh{EXCLUDE_FIND}.' \)'; + } + + foreach my $example (@examples) { + next if excludefile($example); + if (-d $example && $exclude) { + my $basename = basename($example); + my $dir = ($basename eq '.') ? $example : "$example/.."; + chomp($pwd=`pwd`) if not defined($pwd); + # Gracefully handling tmpdir being absolute (-P/...) + my $destdir = $target_dir =~ m{^/} ? $target_dir : "${pwd}/${target_dir}"; + complex_doit("cd '$dir' && " . + "find '$basename' -type f$exclude -print0 | LC_ALL=C sort -z | " . + "xargs -0 -I {} cp --reflink=auto --parents -dp {} ${destdir}"); + } + else { + doit("cp", '--reflink=auto', "-a", $example, $target_dir); + } + } + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installgsettings b/dh_installgsettings new file mode 100755 index 0000000..45b228e --- /dev/null +++ b/dh_installgsettings @@ -0,0 +1,106 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installgsettings - install GSettings overrides and set dependencies + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installgsettings> [S<I<debhelper options>>] [B<--priority=<number>>] + +=head1 DESCRIPTION + +B<dh_installgsettings> is a debhelper program that is responsible for installing +GSettings override files and generating appropriate dependencies on the +GSettings backend. + +The dependency on the backend will be generated in B<${misc:Depends}>. + +=head1 FILES + +=over 4 + +=item debian/I<package>.gsettings-override + +Installed into usr/share/glib-2.0/schemas/10_I<package>.gschema.override in +the package build directory, with "I<package>" replaced by the package name. + +The format of the file is the following: + + [org.gnome.mypackage] + boolean-setting=true + string-setting='string' + ... + +=back + +=head1 OPTIONS + +=over 4 + +=item B<--priority> I<priority> + +Use I<priority> (which should be a 2-digit number) as the override +priority instead of 10. Higher values than ten can be used by +derived distributions (20), blend distributions (50), or site-specific +packages (90). + +=cut + +init(options => { + "priority=s" => \$dh{PRIORITY}, +}); + +my $priority=10; +if (defined $dh{PRIORITY}) { + $priority=$dh{PRIORITY}; +} + +# PROMISE: DH NOOP WITHOUT gsettings-override tmp(usr/share/glib-2.0/schemas) cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + + my $gsettings_schemas_dir = "$tmp/usr/share/glib-2.0/schemas/"; + + my $override = pkgfile($package,"gsettings-override"); + if ($override ne '') { + install_dir($gsettings_schemas_dir); + install_file($override, + "$gsettings_schemas_dir/${priority}_$package.gschema.override"); + } + + if (-d "$gsettings_schemas_dir") { + # Get a list of the schemas + my $schemas = qx_cmd('find', $gsettings_schemas_dir, '-type', 'f', + '(', '-name', '*.xml', '-o', '-name', '*.override', + ')', '-printf', '%P'); + if ($schemas ne '') { + addsubstvar($package, "misc:Depends", "dconf-gsettings-backend | gsettings-backend"); + } + } +} + +=back + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Laurent Bigonville <bigon@debian.org>, +Josselin Mouette <joss@debian.org> + +=cut + diff --git a/dh_installifupdown b/dh_installifupdown new file mode 100755 index 0000000..3d4cd93 --- /dev/null +++ b/dh_installifupdown @@ -0,0 +1,82 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installifupdown - install if-up and if-down hooks + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installifupdown> [S<I<debhelper options>>] [B<--name=>I<name>] + +=head1 DESCRIPTION + +B<dh_installifupdown> is a debhelper program that is responsible for installing +F<if-up>, F<if-down>, F<if-pre-up>, and F<if-post-down> hook scripts into package build +directories. + +=head1 FILES + +=over 4 + +=item debian/I<package>.if-up + +=item debian/I<package>.if-down + +=item debian/I<package>.if-pre-up + +=item debian/I<package>.if-post-down + +These files are installed into etc/network/if-*.d/I<package> in +the package build directory. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<--name=>I<name> + +Look for files named F<debian/package.name.if-*> and install them as +F<etc/network/if-*/name>, instead of using the usual files and installing them +as the package name. + +=back + +=cut + +init(); + +# PROMISE: DH NOOP WITHOUT if-pre-up if-up if-down if-post-down cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + + foreach my $script (qw(pre-up up down post-down)) { + my $file=pkgfile($package, "if-$script"); + if ($file ne '') { + install_dir("$tmp/etc/network/if-$script.d"); + install_prog($file,"$tmp/etc/network/if-$script.d/".pkgfilename($package)); + } + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installinfo b/dh_installinfo new file mode 100755 index 0000000..3a25542 --- /dev/null +++ b/dh_installinfo @@ -0,0 +1,133 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installinfo - install info files + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installinfo> [S<I<debhelper options>>] [B<-A>] [S<I<file> ...>] + +=head1 DESCRIPTION + +B<dh_installinfo> is a debhelper program that is responsible for installing +info files into F<usr/share/info> in the package build directory. + +From debhelper compatibility level 11 on, B<dh_install> will fall back to +looking in F<debian/tmp> for files, if it does not find them in the current +directory (or wherever you've told it to look using B<--sourcedir>). + +=head1 FILES + +=over 4 + +=item debian/I<package>.info + +List info files to be installed. + +Supports substitution variables in compat 13 and later as +documented in L<debhelper(7)>. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-A>, B<--all> + +Install all files specified by command line parameters in ALL packages +acted on. + +=item B<--sourcedir=>I<dir> + +Look in the specified directory for files to be installed. This option +requires compat 11 or later (it is silently ignored in compat 10 or earlier). + +Note that this is not the same as the B<--sourcedirectory> option used +by the B<dh_auto_>I<*> commands. You rarely need to use this option, since +B<dh_installinfo> automatically looks for files in F<debian/tmp> in debhelper +compatibility level 11 and above. + +=item I<file> ... + +Install these info files into the first package acted on. (Or in +all packages if B<-A> is specified). + +=back + +=cut + +init(options => { + "sourcedir=s" => \$dh{SOURCEDIR}, +}); + +# PROMISE: DH NOOP WITHOUT pkgfile-logged(info) cli-options() tmp(usr/share/info/dir) + +my $default_error_handler = compat(10) ? \&glob_expand_error_handler_reject_nomagic_warn_discard : \&glob_expand_error_handler_reject; +my $nodocs = is_build_profile_active('nodoc') || get_buildoption('nodoc') ? 1 : 0; +# We cannot assume documentation is built under nodoc, but if it is we must flag it as handled +# or dh_missing might make noise. +$default_error_handler = \&glob_expand_error_handler_silently_ignore if $nodocs; + +foreach my $package (getpackages()) { + my $tmp=tmpdir($package); + my $file=pkgfile($package,"info"); + my @search_dirs = ('.'); + my @ignored_files; + my $skip_install = process_pkg($package) ? 0 : 1; + my $error_handler = $skip_install ? \&glob_expand_error_handler_silently_ignore : $default_error_handler; + @search_dirs = ($dh{SOURCEDIR} // '.', default_sourcedir($package)) if not compat(10); + + my @info; + + if ($file) { + @info = filearray($file, \@search_dirs, $error_handler) if $file; + } + + if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) { + push @info, @ARGV; + } + + # Pretend that we handled usr/share/info/dir. We do not want to install it as it causes + # file conflicts between packages and we also do not want dh_missing to drop a bomb on + # the user for it. + # + # See #971036 + for my $path (@search_dirs) { + if ( -e "${path}/usr/share/info/dir" ) { + push(@ignored_files, 'usr/share/info/dir'); + last; + } + } + + log_installed_files($package, @info, @ignored_files); + + next if $skip_install or $nodocs; + + if (@info) { + install_dir("$tmp/usr/share/info"); + xargs(\@info, "cp", '--reflink=auto', XARGS_INSERT_PARAMS_HERE, "$tmp/usr/share/info"); + doit("chmod","-R", "u+rw,go=rX","$tmp/usr/share/info/"); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installinit b/dh_installinit new file mode 100755 index 0000000..13df432 --- /dev/null +++ b/dh_installinit @@ -0,0 +1,427 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installinit - install service init files into package build directories + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; +use File::Find; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installinit> [S<I<debhelper options>>] [B<--name=>I<name>] [B<-n>] [B<-R>] [B<-r>] [B<-d>] [S<B<--> I<params>>] + +=head1 DESCRIPTION + +B<dh_installinit> is a debhelper program that is responsible for +installing init scripts with associated defaults files. In +compatibility levels up to and including 10, B<dh_installinit> will +also install some systemd related files provided by the debian +packaging (see the L</FILES> section below). In compatibility levels +up to and including 11, B<dh_installinit> will also handle upstart +jobs provided in the debian packaging (see the L</FILES> for more +information on this as well). + +It also automatically generates the F<postinst> and F<postrm> and F<prerm> +commands needed to set up the symlinks in F</etc/rc*.d/> to start and stop +the init scripts. + +In compat 10 or earlier: If a package only ships a systemd service +file and no sysvinit script is provided, you may want to exclude the +call to dh_installinit for that package (e.g. via B<-N>). Otherwise, +you may get warnings from lintian about init.d scripts not being +included in the package. + +=head1 FILES + +=over 4 + +=item debian/I<package>.init + +If this exists, it is installed into etc/init.d/I<package> in the package +build directory. + +=item debian/I<package>.default + +If this exists, it is installed into etc/default/I<package> in the package +build directory. + +=item debian/I<package>.upstart + +In compatibility level 11, this file will trigger an error with a reminder +about ensuring the proper removal of the upstart file in the previous package +version. Please consider using the "rm_conffile" feature from +L<dh_installdeb(1)> to ensure the proper removal of previous upstart files. + +In compatibility level 10, if this file exists, it is installed into +etc/init/I<package>.conf in the package build directory. + +=item debian/I<package>.service + +If this exists, it is installed into F<< lib/systemd/system/I<package>.service >> in +the package build directory. Only used in compat levels 10 and below. + +=item debian/I<package>.tmpfile + +If this exists, it is installed into usr/lib/tmpfiles.d/I<package>.conf in the +package build directory. Only used in compat levels 10 and below. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-n>, B<--no-scripts> + +Do not modify F<postinst>/F<postrm>/F<prerm> scripts. + +=item B<-o>, B<--only-scripts> + +Only modify F<postinst>/F<postrm>/F<prerm> scripts, do not actually install +any init script, default files, upstart job or systemd service file. May be +useful if the file is shipped and/or installed by upstream in a way that +doesn't make it easy to let B<dh_installinit> find it. + +B<Caveat>: This will bypass all the regular checks and +I<unconditionally> modify the scripts. You will almost certainly want +to use this with B<-p> to limit, which packages are affected by the +call. Example: + + override_dh_installinit: + dh_installinit -pfoo --only-scripts + dh_installinit --remaining + +=item B<-R>, B<--restart-after-upgrade> + +Do not stop the init script until after the package upgrade has been +completed. This is the default behaviour in compat 10. + +In early compat levels, the default was to stop the script in the +F<prerm>, and starts it again in the F<postinst>. + +This can be useful for daemons that should not have a possibly long +downtime during upgrade. But you should make sure that the daemon will not +get confused by the package being upgraded while it's running before using +this option. + +=item B<--no-restart-after-upgrade> + +Undo a previous B<--restart-after-upgrade> (or the default of compat +10). If no other options are given, this will cause the service to be +stopped in the F<prerm> script and started again in the F<postinst> +script. + +=item B<-r>, B<--no-stop-on-upgrade>, B<--no-restart-on-upgrade> + +Do not stop init script on upgrade. This has the side-effect of not +restarting the service as a part of the upgrade. + +If you want to restart the service with minimal downtime, please use +B<--restart-after-upgrade> (default in compat 10 or later). If you want +the service to be restarted but be stopped during the upgrade, then please +use B<--no-restart-after-upgrade> (note the "after-upgrade"). + +Note that the B<--no-restart-on-upgrade> alias is deprecated and will +be removed in compat 12. This is to avoid confusion with the +B<--no-restart-after-upgrade> option. The B<--no-stop-on-upgrade> +variant was introduced in debhelper 10.2 (included in Debian stretch). + +=item B<--no-start> + +Do not start the init script on install or upgrade, or stop it on removal. +Only call B<update-rc.d>. Useful for rcS scripts. + +=item B<--no-enable> + +Disable the init script on purge, but do not enable them on install. +This implies a versioned dependency on B<< init-system-helpers (E<gt>= +1.51) >> as it is the first (functional) version that supports +B<< update-rc.d E<lt>scriptE<gt> defaults-disabled >>. + +B<Note> that this option does not affect whether the services are +started. Please remember to also use B<--no-start> if the service +should not be started. + +Cannot be combined with B<-u>I<params>, +B<--update-rcd-params=>I<params>, or B<--> I<params>. + +=item B<-d>, B<--remove-d> + +Remove trailing B<d> from the name of the package, and use the result for the +filename the upstart job file is installed as in F<etc/init/> , and for the +filename the init script is installed as in etc/init.d and the default file +is installed as in F<etc/default/>. This may be useful for daemons with names +ending in B<d>. (Note: this takes precedence over the B<--init-script> parameter +described below.) + +=item B<-u>I<params> B<--update-rcd-params=>I<params> + +=item B<--> I<params> + +Pass I<params> to L<update-rc.d(8)>. If not specified, B<defaults> (or +B<defaults-disabled> with B<--no-enable>) will be passed to +L<update-rc.d(8)>. + +Cannot be combined with B<--no-enable>. + +=item B<--name=>I<name> + +Install the init script (and default file) as well as upstart job file +using the filename I<name> instead of the default filename, which is +the package name. When this parameter is used, B<dh_installinit> looks +for and installs files named F<debian/package.name.init>, +F<debian/package.name.default> and F<debian/package.name.upstart> +instead of the usual F<debian/package.init>, F<debian/package.default> and +F<debian/package.upstart>. + +=item B<--init-script=>I<scriptname> + +Use I<scriptname> as the filename the init script is installed as in +F<etc/init.d/> (and also use it as the filename for the defaults file, if it +is installed). If you use this parameter, B<dh_installinit> will look to see +if a file in the F<debian/> directory exists that looks like +F<package.scriptname> and if so will install it as the init script in +preference to the files it normally installs. + +This parameter is deprecated, use the B<--name> parameter instead. This +parameter is incompatible with the use of upstart jobs. + +=item B<--error-handler=>I<function> + +Call the named shell I<function> if running the init script fails. The +function should be provided in the F<prerm> and F<postinst> scripts, before the +B<#DEBHELPER#> token. + +=back + +=head1 NOTES + +Note that this command is not idempotent. L<dh_prep(1)> should be called +between invocations of this command. Otherwise, it may cause multiple +instances of the same text to be added to maintainer scripts. + +=cut + +$dh{RESTART_AFTER_UPGRADE} = ''; +$dh{NO_START} = ''; + +init(options => { + "r" => \$dh{R_FLAG}, + 'no-stop-on-upgrade' => \$dh{R_FLAG}, + "no-restart-on-upgrade" => sub { + $dh{R_FLAG} = 1; + deprecated_functionality("--no-restart-on-upgrade has been renamed to --no-stop-on-upgrade", + 12); + }, + "no-start" => \$dh{NO_START}, + "R|restart-after-upgrade!" => \$dh{RESTART_AFTER_UPGRADE}, + "init-script=s" => \$dh{INIT_SCRIPT}, + "update-rcd-params=s", => \$dh{U_PARAMS}, + "remove-d" => \$dh{D_FLAG}, + "no-enable" => \$dh{NO_ENABLE}, +}); + +if ($dh{RESTART_AFTER_UPGRADE} eq '') { + $dh{RESTART_AFTER_UPGRADE} = 1 if not defined($dh{R_FLAG}) and $dh{NO_START} eq ''; +} + +# PROMISE: DH NOOP WITHOUT service tmpfile default upstart init init.d tmp(usr/lib/tmpfiles.d) tmp(etc/tmpfiles.d) cli-options(--init-script|-d|--remove-d|-o|--only-scripts) + +my %snippet_options = ('snippet-order' => 'service'); + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + + # Figure out what filename to install it as. + my $script; + my $scriptsrc; + my $jobfile=$package; + if (defined $dh{NAME}) { + $jobfile=$script=$scriptsrc=$dh{NAME}; + } + elsif ($dh{D_FLAG}) { + # -d on the command line sets D_FLAG. We will + # remove a trailing 'd' from the package name and + # use that as the name. + $script=$package; + if ($script=~m/(.*)d$/) { + $jobfile=$script=$1; + } + else { + warning("\"$package\" has no final d' in its name, but -d was specified."); + } + $scriptsrc=$script; + } + elsif ($dh{INIT_SCRIPT}) { + $script=$dh{INIT_SCRIPT}; + $scriptsrc=$script; + } + else { + $script=$package; + if (compat(9)) { + $scriptsrc=$script; + } + else { + $scriptsrc="init"; + } + } + + my $service=''; + $service=pkgfile($package,"service") if compat(10); + if ($service ne '' && ! $dh{ONLYSCRIPTS}) { + my $path="$tmp/lib/systemd/system"; + install_dir($path); + install_file($service, "$path/$script.service"); + } + + my $tmpfile=''; + $tmpfile=pkgfile($package,"tmpfile") if compat(10); + if ($tmpfile ne '' && ! $dh{ONLYSCRIPTS}) { + my $path="$tmp/usr/lib/tmpfiles.d"; + install_dir($path); + install_file($tmpfile, "$path/$script.conf"); + } + + my $job=pkgfile($package,"upstart"); + if ($job ne '' and not compat(11)) { + isnative($package); # For the side-effect of setting $dh{VERSION} + warning("Detected an upstart file; these are no longer supported by dh_installinit in compat 11"); + warning("Please ensure a proper removal by adding a \"rm_conffile\" line in debian/<pkg>.maintscript"); + warning("Example maintscript line:"); + warning(" rm_conffile /etc/init/${jobfile}.conf $dh{VERSION}"); + warning("(Note: the example is a best-effort guess and it is not always correct! Please verify before use)"); + warning("see \"man dh_installdeb\" for more information about the maintscript file"); + warning(""); + error("upstart jobs are no longer supported! Please remove $job and check if you need to add a conffile removal"); + } + if ($job ne '' && ! $dh{ONLYSCRIPTS}) { + install_dir("$tmp/etc/init"); + install_file($job, "$tmp/etc/init/$jobfile.conf"); + } + + my $default=pkgfile($package,'default'); + if ($default ne '' && ! $dh{ONLYSCRIPTS}) { + install_dir("$tmp/etc/default"); + install_file($default, "$tmp/etc/default/$script"); + } + + my $init=pkgfile($package,$scriptsrc) || pkgfile($package,"init") || + pkgfile($package,"init.d"); + + if ($init ne '' && ! $dh{ONLYSCRIPTS}) { + install_dir("$tmp/etc/init.d"); + install_prog($init,"$tmp/etc/init.d/$script"); + } + + if ($dh{INIT_SCRIPT} && $job ne '' && $init ne '') { + error("Can't use --init-script with an upstart job"); + } + + if (compat(10) && !$dh{NOSCRIPTS}) { + # Include postinst-init-tmpfiles if the package ships any files + # in /usr/lib/tmpfiles.d or /etc/tmpfiles.d + my @tmpfiles; + find({ + wanted => sub { + my $name = $File::Find::name; + return unless -f $name; + $name =~ s/^\Q$tmp\E//g; + if ($name =~ m,^/usr/lib/tmpfiles\.d/, || + $name =~ m,^/etc/tmpfiles\.d/,) { + push(@tmpfiles, basename($name)); + } + }, + no_chdir => 1, + }, $tmp); + if (@tmpfiles > 0) { + # Not migrated to hashref based autoscripts. This will + # happen as people migrate to dh_installsystemd. + autoscript($package,"postinst", "postinst-init-tmpfiles", + "s,#TMPFILES#," . join(" ", sort @tmpfiles).",g"); + } + } + + if ($service ne '' || $job ne '' || $init ne '' || $dh{ONLYSCRIPTS}) { + # This is set by the -u "foo" command line switch, it's + # the parameters to pass to update-rc.d. If not set, + # we have to say "defaults". + my $params = 'defaults'; + my $update_rcd_params = compat(11) ? '' : '--skip-systemd-native '; + if ($dh{NO_ENABLE}) { + $params = 'defaults-disabled'; + addsubstvar($package, "misc:Depends", "init-system-helpers (>= 1.51)"); + } + + if (defined($dh{U_PARAMS}) and @{$dh{U_PARAMS}}) { + error("--no-enable and -- params/-u/--update-rcd-params/ are mutually exclusive") if $dh{NO_ENABLE}; + $params=join(' ',@{$dh{U_PARAMS}}); + } + + if (! $dh{NOSCRIPTS}) { + my $replace = { + 'SCRIPT' => $script, + 'INITPARMS' => $params, + 'ERROR_HANDLER' => $dh{ERROR_HANDLER}, + 'INVOKE_RCD_PARAMS' => $update_rcd_params, + }; + autoscript($package, 'preinst', 'preinst-init-chmod', $replace, \%snippet_options); + + if (! $dh{NO_START}) { + if ($dh{RESTART_AFTER_UPGRADE}) { + # update-rc.d, and restart (or + # start if new install) script + autoscript($package, 'postinst', 'postinst-init-restart', $replace, \%snippet_options); + } + else { + # update-rc.d, and start script + autoscript($package, 'postinst', 'postinst-init', $replace, \%snippet_options); + } + + autoscript($package, 'preinst', 'preinst-init-stop', $replace, \%snippet_options) + unless ($dh{R_FLAG} || $dh{RESTART_AFTER_UPGRADE}); + + # stops script only on remove + autoscript($package, 'prerm', 'prerm-init-norestart', $replace, \%snippet_options); + + # The --skip-systemd-native option requires + # init-system-helpers (>= 1.54) and since we need it + # from prerm we need init-system-helpers to have been + # unpacked before the package is being unpacked. + addsubstvar($package, 'misc:Pre-Depends', 'init-system-helpers (>= 1.54~)') + if $update_rcd_params !~ m/^\s*$/; + } + else { + # just update-rc.d + autoscript($package,"postinst", "postinst-init-nostart", $replace, \%snippet_options); + } + + # removes rc.d links + autoscript($package,"postrm","postrm-init", + { 'SCRIPT' => $script, 'ERROR_HANDLER' => $dh{ERROR_HANDLER} }, + \%snippet_options); + } + } +} + +=head1 SEE ALSO + +L<debhelper(7)>, L<dh_installsystemd(1)> + +This program is a part of debhelper. + +=head1 AUTHORS + +Joey Hess <joeyh@debian.org> + +Steve Langasek <steve.langasek@canonical.com> + +Michael Stapelberg <stapelberg@debian.org> + +=cut diff --git a/dh_installinitramfs b/dh_installinitramfs new file mode 100755 index 0000000..37fb27f --- /dev/null +++ b/dh_installinitramfs @@ -0,0 +1,103 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installinitramfs - install initramfs hooks and setup maintscripts + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installinitramfs> [S<B<debhelper options>>] [B<-n>] + +=head1 DESCRIPTION + +B<dh_installinitramfs> is a debhelper program that is responsible for +installing Debian package provided initramfs hooks. + +If B<dh_installinitramfs> installs or detects one or more initramfs +hooks in the package, then it also automatically generates the noawait +trigger B<update-initframfs> command needed to interface with the +Debian initramfs system. This trigger is inserted into the +packaging by L<dh_installdeb(1)>. + +=head1 FILES + +=over 4 + +=item debian/I<package>.initramfs-hook + +Assumed to be an initramfs hook that will be installed into F<< +usr/share/initramfs-tools/hooks/I<package> >> in the package build +directory. See B<HOOK SCRIPTS> in L<initramfs-tools(8)> for more +information about initramfs hooks. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-n>, B<--no-scripts> + +Do not add the B<update-initramfs> trigger even if it seems like the package +might need it. The option is called B<--no-scripts> for historical +reasons as B<dh_installinitramfs> would previously generate maintainer +scripts that called B<update-initramfs -u>. + +Use this option, if you need to interface with the B<update-initramfs> +system that is not satisfied by the noawait trigger (e.g. because you need +the extra guarantees and head-aches of a await trigger). + +=back + +=head1 NOTES + +Note that this command is not idempotent. L<dh_prep(1)> should be called +between invocations of this command. Otherwise, it may cause multiple +instances of the same text to be added to triggers file. + +=cut + +init(); + +# PROMISE: DH NOOP WITHOUT initramfs-hook tmp(usr/share/initramfs-tools/hooks) cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp = tmpdir($package); + my $hook_script = pkgfile($package, 'initramfs-hook'); + my $has_hooks; + my $hook_dir = "${tmp}/usr/share/initramfs-tools/hooks"; + + if ($hook_script ne '') { + install_dir($hook_dir); + install_prog($hook_script, "${hook_dir}/${package}"); + $has_hooks = 1; + } elsif (-d $hook_dir and not is_empty_dir($hook_dir)) { + $has_hooks = 1; + } + + if ($has_hooks && ! $dh{NOSCRIPTS}) { + autotrigger($package, 'activate-noawait', 'update-initramfs'); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> +L<update-initramfs(8)> +L<initramfs-tools(8)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Niels Thykier <niels@thykier.net> + +=cut diff --git a/dh_installlogcheck b/dh_installlogcheck new file mode 100755 index 0000000..bf1f779 --- /dev/null +++ b/dh_installlogcheck @@ -0,0 +1,91 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installlogcheck - install logcheck rulefiles into etc/logcheck/ + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installlogcheck> [S<I<debhelper options>>] + +=head1 DESCRIPTION + +B<dh_installlogcheck> is a debhelper program that is responsible for +installing logcheck rule files. + +=head1 FILES + +=over 4 + +=item debian/I<package>.logcheck.cracking + +=item debian/I<package>.logcheck.violations + +=item debian/I<package>.logcheck.violations.ignore + +=item debian/I<package>.logcheck.ignore.workstation + +=item debian/I<package>.logcheck.ignore.server + +=item debian/I<package>.logcheck.ignore.paranoid + +Each of these files, if present, are installed into corresponding +subdirectories of F<etc/logcheck/> in package build directories. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<--name=>I<name> + +Look for files named F<debian/package.name.logcheck.*> and install +them into the corresponding subdirectories of F<etc/logcheck/>, but +use the specified name instead of that of the package. + +=back + +=cut + +init(); + +# PROMISE: DH NOOP WITHOUT logcheck.cracking logcheck.violations logcheck.violations.ignore logcheck.ignore.workstation logcheck.ignore.server logcheck.ignore.paranoid cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + + foreach my $type (qw{ignore.d.workstation ignore.d.server + ignore.d.paranoid cracking.d + violations.d violations.ignore.d}) { + my $typenod=$type; + $typenod=~s/\.d//; + my $logcheck=pkgfile($package,"logcheck.$typenod"); + if ($logcheck) { + my $packagenodot=pkgfilename($package); # run-parts.. + $packagenodot=~s/\./_/g; + install_dir("$tmp/etc/logcheck/$type"); + install_file($logcheck, "$tmp/etc/logcheck/$type/$packagenodot"); + } + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Jon Middleton <jjm@debian.org> + +=cut diff --git a/dh_installlogrotate b/dh_installlogrotate new file mode 100755 index 0000000..fc3d60a --- /dev/null +++ b/dh_installlogrotate @@ -0,0 +1,63 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installlogrotate - install logrotate config files + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installlogrotate> [S<I<debhelper options>>] [B<--name=>I<name>] + +=head1 DESCRIPTION + +B<dh_installlogrotate> is a debhelper program that is responsible for installing +logrotate config files into F<etc/logrotate.d> in package build directories. +Files named F<debian/package.logrotate> are installed. + +=head1 OPTIONS + +=over 4 + +=item B<--name=>I<name> + +Look for files named F<debian/package.name.logrotate> and install them as +F<etc/logrotate.d/name>, instead of using the usual files and installing them +as the package name. + +=back + +=cut + +init(); + +# PROMISE: DH NOOP WITHOUT logrotate cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + my $file=pkgfile($package,"logrotate"); + + if ($file) { + install_dir("$tmp/etc/logrotate.d"); + install_file($file,"$tmp/etc/logrotate.d/".pkgfilename($package)); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installman b/dh_installman new file mode 100755 index 0000000..015d439 --- /dev/null +++ b/dh_installman @@ -0,0 +1,430 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installman - install man pages into package build directories + +=cut + +use strict; +use warnings; +use File::Find; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installman> [S<I<debhelper options>>] [S<I<manpage> ...>] + +=head1 DESCRIPTION + +B<dh_installman> is a debhelper program that handles installing man +pages into the correct locations in package build directories. + +In compat 10 and earlier, this program was primarily for when +upstream's build system does not properly install them as a part of +its install step (or it does not have an install step). In compat 11 +and later, it also supports the default searchdir plus --sourcedir +like dh_install(1) and has the advantage that it respects the nodoc +build profile (unlike dh_install(1)). + +Even if you prefer to use L<dh_install(1)> for installing the manpages, +B<dh_installman> can still be useful for converting the manpage encoding +to UTF-8 and for converting F<.so> links (as described below). However, +that part happens automatically without any explicit configuration. + + +You tell B<dh_installman> what man pages go in your packages, and it figures out +where to install them based on the section field in their B<.TH> or +B<.Dt> line. If you have a properly formatted B<.TH> or B<.Dt> line, +your man page will be installed into the right directory, with the +right name (this includes proper handling of pages with a subsection, +like B<3perl>, which are placed in F<man3>, and given an extension of +F<.3perl>). If your B<.TH> or B<.Dt> line is incorrect or missing, the +program may guess wrong based on the file extension. + +It also supports translated man pages, by looking for extensions +like F<.ll.8> and F<.ll_LL.8>, or by use of the B<--language> switch. + +If B<dh_installman> seems to install a man page into the wrong section or with +the wrong extension, this is because the man page has the wrong section +listed in its B<.TH> or B<.Dt> line. Edit the man page and correct the +section, and B<dh_installman> will follow suit. See L<man(7)> for details +about the B<.TH> section, and L<mdoc(7)> for the B<.Dt> section. If +B<dh_installman> seems to install a man page into a directory +like F</usr/share/man/pl/man1/>, that is because your program has a +name like F<foo.pl>, and B<dh_installman> assumes that means it is translated +into Polish. Use B<--language=C> to avoid this. + +After the man page installation step, B<dh_installman> will check to see if +any of the man pages in the temporary directories of any of the packages it +is acting on contain F<.so> links. If so, it changes them to symlinks. + +Also, B<dh_installman> will use man to guess the character encoding of each +manual page and convert it to UTF-8. If the guesswork fails for some +reason, you can override it using an encoding declaration. See +L<manconv(1)> for details. + +From debhelper compatibility level 11 on, B<dh_install> will fall back to +looking in F<debian/tmp> for files, if it does not find them in the current +directory (or wherever you've told it to look using B<--sourcedir>). + +=head1 FILES + +=over 4 + +=item debian/I<package>.manpages + +Lists man pages to be installed. + +Supports substitution variables in compat 13 and later as +documented in L<debhelper(7)>. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-A>, B<--all> + +Install all files specified by command line parameters in ALL packages +acted on. + +=item B<--language=>I<ll> + +Use this to specify that the man pages being acted on are written in the +specified language. + +=item B<--sourcedir=>I<dir> + +Look in the specified directory for files to be installed. This option +requires compat 11 or later (it is silently ignored in compat 10 or earlier). + +Note that this is not the same as the B<--sourcedirectory> option used +by the B<dh_auto_>I<*> commands. You rarely need to use this option, since +B<dh_installman> automatically looks for files in F<debian/tmp> in debhelper +compatibility level 11 and above. + +=item I<manpage> ... + +Install these man pages into the first package acted on. (Or in all +packages if B<-A> is specified). + +=back + +=head1 EXAMPLES + +An example F<debian/manpages> file could look like this: + + doc/man/foo.1 + # Translations + doc/man/foo.da.1 + doc/man/foo.de.1 + doc/man/foo.fr.1 + # NB: The following line is considered a polish translation + # of "foo.1" (and not a manpage written in perl called "foo.pl") + doc/man/foo.pl.1 + # ... + +=head1 NOTES + +An older version of this program, L<dh_installmanpages(1)>, is still used +by some packages, and so is still included in debhelper. +It is, however, deprecated, due to its counterintuitive and inconsistent +interface. Use this program instead. + +=cut + +init(options => { + "language=s" => \$dh{LANGUAGE}, + "sourcedir=s" => \$dh{SOURCEDIR}, +}); + + +# PROMISE: DH NOOP WITHOUT pkgfile-logged(manpages) tmp(usr/share/man) cli-options() + +my (@sofiles, @sodests); +my @all_packages = getpackages(); + +my $default_error_handler = compat(10) ? \&glob_expand_error_handler_reject_nomagic_warn_discard : \&glob_expand_error_handler_reject; +my $nodocs = is_build_profile_active('nodoc') || get_buildoption('nodoc') ? 1 : 0; +# We cannot assume documentation is built under nodoc, but if it is we must flag it as handled +# or dh_missing might make noise. +$default_error_handler = \&glob_expand_error_handler_silently_ignore if $nodocs; + +on_items_in_parallel(\@all_packages, sub { + + foreach my $package (@_) { + next if is_udeb($package); + + my $tmp = tmpdir($package); + my $file = pkgfile($package, "manpages"); + my @manpages; + my @search_dirs = ('.'); + my $skip_install = process_pkg($package) ? 0 : 1; + my $error_handler = $skip_install ? \&glob_expand_error_handler_silently_ignore : $default_error_handler; + @search_dirs = ($dh{SOURCEDIR} // '.', default_sourcedir($package)) if not compat(10); + + @manpages = filearray($file, \@search_dirs, $error_handler) if $file; + + if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) { + push @manpages, @ARGV; + } + + log_installed_files($package, @manpages); + + next if $skip_install or $nodocs; + + foreach my $page (@manpages) { + my $basename = basename($page); + + # Support compressed pages. + my $gz = ''; + if ($basename =~ m/(.*)(\.gz)/) { + $basename = $1; + $gz = $2; + } + + my ($fd, $section); + # See if there is a .TH or .Dt entry in the man page. If so, + # we'll pull the section field from that. + if ($gz) { + $fd = open_gz($page) or error("open $page failed: $!"); + } + else { + open($fd, '<', $page) or error("open $page failed: $!"); + } + while (<$fd>) { + if (/^\.TH\s+\S+\s+"?(\d+[^"\s]*)"?/ || + /^\.Dt\s+\S+\s+(\d+[^\s]*)/) { + $section = $1; + if ($section =~ m/^\d+[.]\d+/) { + warning("Ignoring section defined in TH/Dt for ${page} as it looks like a version number: ${section}"); + $section = undef; + } + last; + } + } + close($fd); + # Failing that, we can try to get it from the filename. + if (!$section) { + ($section) = $basename =~ m/\.([1-9]\w*)$/; + } + + # Now get the numeric component of the section. + my ($realsection) = $section =~ m/^(\d+)/ if defined $section; + if (!$realsection or ($realsection < 0 or $realsection > 9)) { + warning("Section for ${page} is computed as \"${section}\", which is not a valid section") + if defined($section); + error("Could not determine section for $page"); + } + + # Get the man page's name -- everything up to the last dot. + my ($instname) = $basename =~ m/^(.*)\./; + + my $destdir = "$tmp/usr/share/man/man$realsection/"; + my $langcode; + if (!defined $dh{LANGUAGE} || !exists $dh{LANGUAGE}) { + if (not compat(10) and $page =~ m{/man/(?:([a-z][a-z](?:_[A-Z][A-Z])?)(?:\.[^/]+)?)?/man[1-9]/}) { + # If it looks like it was installed in a proper man dir, assume the language + # from that is correct. + $langcode = $1; + } else { + # Translated man pages are typically specified by adding the + # language code to the filename, so detect that and + # redirect to appropriate directory, stripping the code. + ($langcode) = $basename =~ m/\.([a-z][a-z](?:_[A-Z][A-Z])?)\.(?:[1-9]|man)/; + # Avoid false positives such as /usr/share/man/man8/libnss_myhostname.so.2.8 + if (defined $langcode && $langcode eq 'so' && $basename =~ /^lib.*\.so(\.[0-9]+)*$/) { + $langcode = ''; + } + } + } elsif ($dh{LANGUAGE} ne 'C') { + $langcode = $dh{LANGUAGE}; + } + + if (defined $langcode && $langcode ne '') { + # Strip the language code from the instname. + $instname =~ s/\.$langcode$//; + } + + if (defined $langcode && $langcode ne '') { + $destdir = "$tmp/usr/share/man/$langcode/man$realsection/"; + } + $destdir =~ tr:/:/:s; # just for looks + my $instpage = "$destdir$instname.$section"; + + next if -l $instpage; + + install_dir($destdir); + if ($gz) { + doit({ stdout => $instpage }, 'zcat', $page); + } + else { + install_file($page, $instpage); + } + } + + # Now the .so conversion. + @sofiles = @sodests = (); + foreach my $dir (qw{usr/share/man}) { + if (-e "$tmp/$dir") { + find(\&find_so_man, "$tmp/$dir"); + } + } + foreach my $sofile (@sofiles) { + my $sodest = shift(@sodests); + rm_files($sofile); + make_symlink_raw_target($sodest, $sofile); + } + } + +}); + +# Now utf-8 conversion. +my $has_man_recode = 0; +$has_man_recode = 1 if has_man_db_tool('man-recode'); + +if ($has_man_recode || has_man_db_tool('man')) { + my (@manpages_to_reencode, @issues); + for my $package (@{$dh{DOPACKAGES}}) { + next if is_udeb($package); + my $tmp = tmpdir($package); + foreach my $dir (qw{usr/share/man}) { + next unless -e "$tmp/$dir"; + my %seen; + my $wanted = sub { + my $path = $File::Find::name; + return if -l $path || !-f _; + if ($path =~ m/\.dh-new$/) { + push(@issues, $path); + return; + } + my $uncompressed_name = $path; + $uncompressed_name =~ s/\.(?:gz|Z)$//; + if (exists($seen{$uncompressed_name})) { + my $msg = "Multiple definitions for manpage ${uncompressed_name} via different compressions."; + my @values = sort ($path, $seen{$uncompressed_name}); + my $warn_msg = $msg . ' Picking ' . $values[0] . ' as the canonical definition.'; + my $error_msg = $msg . ' Please ensure there is at most one definition.'; + deprecated_functionality($warn_msg, 13, $error_msg); + $path = $values[0]; + warn("Removing conflicting definition of ${uncompressed_name} (" . $values[1] + . ') to ensure deterministic behaviour.'); + rm_files($values[1]); + } + $seen{$uncompressed_name} = $path; + }; + find({ + no_chdir => 1, + wanted => $wanted, + }, "$tmp/$dir"); + push(@manpages_to_reencode, sort(values(%seen))); + } + + if (@issues) { + warning("Removing temporary manpages from another dh_installman instance"); + rm_files(@issues); + warning("Possibly race-condition detected or left-overs from an interrupted dh_installman (e.g. with ^C)"); + error("Please ensure there are no parallel dh_installman's running (for this pkg) and then re-run dh_installman"); + } + } + if (@manpages_to_reencode) { + on_items_in_parallel(\@manpages_to_reencode, \&reencode_manpages); + } +} else { + # Should only occur during debhelper building itself (to avoid a B-D on man-db). + warning("man is not available. Skipping re-encode of UTF-8 manpages") +} + +# Check if a file is a .so man page, for use by File::Find. +sub find_so_man { + # The -s test is because a .so file tends to be small. We don't want + # to open every man page. 1024 is arbitrary. + if (! -f $_ || -s _ > 1024 || -s _ == 0) { + return; + } + + # Test first line of file for the .so thing. + my $fd; + if (/\.gz$/) { + $fd = open_gz($_) or error("open $_ failed: $!"); + } + else { + open($fd, '<', $_) || error("open $_ failed: $!"); + } + my $l = <$fd>; + close($fd); + + if (! defined $l) { + error("failed to read $_"); + } + + if ($l=~m/\.so\s+(.*)\s*/) { + my $solink=$1; + # This test is here to prevent links like ... man8/../man8/foo.8 + if (basename($File::Find::dir) eq + dirname($solink)) { + $solink=basename($solink); + } + # A so link with a path is relative to the base of the man + # page hierarchy, but without a path, is relative to the + # current section. + elsif ($solink =~ m!/!) { + $solink="../$solink"; + } + + if (-e $solink || -e "$solink.gz") { + push @sofiles,"$File::Find::dir/$_"; + push @sodests,$solink; + } + } +} + +sub has_man_db_tool { + my ($tool) = @_; + open(my $old_stderr, '>&', *STDERR) or error("dup(STDERR, tmp_fd): $!"); + # Ignore the error; it is intended as noise-reduction. As long as we can restore + # the stderr later, the log will just be slightly more noisy than planned. + open(*STDERR, '>', '/dev/null') or warn("redirect stderr to /dev/null failed: $!"); + + my $res = defined(`$tool --version`); + open(*STDERR, '>&', $old_stderr) or error("dup(tmp_fd, STDERR): $!"); + close($old_stderr); + return $res; +} + +sub reencode_manpages { + my (@manpages) = @_; + if ($has_man_recode) { + xargs(\@manpages, 'man-recode', '--to-code', 'UTF-8', '--suffix', '.dh-new'); + } + for my $manpage (@manpages) { + my $manpage_tmp = "${manpage}.dh-new"; + $manpage_tmp =~ s/\.(?:gz|Z)\.dh-new$/.dh-new/; + if (not $has_man_recode) { + my $manpage_cmd = ($manpage =~ m{^/}) ? $manpage : "./${manpage}"; + doit({ stdout => $manpage_tmp }, 'man', '-l', '--recode', 'UTF-8', $manpage_cmd); + } + # recode uncompresses compressed pages + my $orig = $manpage; + rm_files($orig) if $manpage =~ s/\.(gz|Z)$//; + rename_path($manpage_tmp, $manpage); + } + # Bulk reset permissions of all re-encoded files + xargs(\@manpages, 'chmod', '0644', '--'); +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installmanpages b/dh_installmanpages new file mode 100755 index 0000000..22c669e --- /dev/null +++ b/dh_installmanpages @@ -0,0 +1,208 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installmanpages - old-style man page installer (deprecated) + +=cut + +use strict; +use warnings; +use File::Find; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installmanpages> [S<I<debhelper options>>] [S<I<file> ...>] + +=head1 DESCRIPTION + +B<dh_installmanpages> is a debhelper program that is responsible for +automatically installing man pages into F<usr/share/man/> +in package build directories. + +This is a DWIM-style program, with an interface unlike the rest of +debhelper. It is deprecated, and you are encouraged to use +L<dh_installman(1)> instead. + +B<dh_installmanpages> scans the current directory and all subdirectories for +filenames that look like man pages. (Note that only real files are looked +at; symlinks are ignored.) It uses L<file(1)> to verify that the files are +in the correct format. Then, based on the files' extensions, it installs +them into the correct man directory. + +All filenames specified as parameters will be skipped by B<dh_installmanpages>. +This is useful if by default it installs some man pages that you do not +want to be installed. + +After the man page installation step, B<dh_installmanpages> will check to see +if any of the man pages are F<.so> links. If so, it changes them to symlinks. + +=head1 OPTIONS + +=over 4 + +=item I<file> ... + +Do not install these files as man pages, even if they look like valid man +pages. + +=back + +=head1 BUGS + +B<dh_installmanpages> will install the man pages it finds into B<all> packages +you tell it to act on, since it can't tell what package the man +pages belong in. This is almost never what you really want (use B<-p> to work +around this, or use the much better L<dh_installman(1)> program instead). + +Files ending in F<.man> will be ignored. + +Files specified as parameters that contain spaces in their filenames will +not be processed properly. + +=cut + +warning("This program is deprecated, switch to dh_installman."); + +init(); + +# Check if a file is a man page, for use by File::Find. +my @manpages; +my @allpackages; +sub find_man { + # Does its filename look like a man page? + # .ex files are examples installed by deb-make, + # we don't want those, or .in files, which are + # from configure, nor do we want CVS .#* files. + if (! (-f $_ && /^.*\.[1-9].*$/ && ! /\.(ex|in)$/ && ! /^\.#/)) { + return; + } + + # It's not in a tmp directory is it? + if ($File::Find::dir=~m:debian/.*tmp.*:) { + return; + } + foreach my $dir (@allpackages) { + if ($File::Find::dir=~m:debian/\Q$dir\E:) { + return; + } + } + + # And file does think it's a real man page? + my $type=`file -z $_`; + if ($type !~ m/:.*roff/) { + return; + } + + # Good enough. + push @manpages,"$File::Find::dir/$_"; +} + +# Check if a file is a .so man page, for use by File::Find. +my @sofiles; +my @sodests; +sub find_so_man { + # The -s test is because a .so file tends to be small. We don't want + # to open every man page. 1024 is arbitrary. + if (! -f $_ || -s $_ > 1024) { + return; + } + + # Test first line of file for the .so thing. + open(my $fd, '<', $_); + my $l = <$fd>; + close($fd); + if ($l=~m/\.so\s+(.*)/) { + my $solink=$1; + # This test is here to prevent links like ... man8/../man8/foo.8 + if (basename($File::Find::dir) eq + dirname($solink)) { + $solink=basename($solink); + } + else { + $solink="../$solink"; + } + + push @sofiles,"$File::Find::dir/$_"; + push @sodests,$solink; + } +} + +foreach my $package (@{$dh{DOPACKAGES}}) { + next if is_udeb($package); + + my $tmp=tmpdir($package); + + # Find all filenames that look like man pages. + @manpages=(); + @allpackages=getpackages() if not @allpackages; + find(\&find_man,'.'); # populates @manpages + + foreach my $page (@manpages) { + $page=~s:^\./::; # just for looks + + my $basename=basename($page); + + # Skip all files listed on command line. + my $install=1; + foreach my $skip (@ARGV) { + # Look at basename of what's on connect line + # for backwards compatibility. + if ($basename eq basename($skip)) { + $install=undef; + last; + } + } + + if ($install) { + my $extdir="share"; + + my ($section)=$basename=~m/\.([1-9])/; + + my $destdir="$tmp/usr/$extdir/man/man$section/"; + + # Handle translated man pages. + my $instname=$basename; + my ($langcode)=$basename=~m/\.([a-z][a-z])\.([1-9])/; + if (defined $langcode && $langcode ne '') { + $destdir="$tmp/usr/$extdir/man/$langcode/man$section/"; + $instname=~s/\.$langcode\./\./; + } + + $destdir=~tr:/:/:s; # just for looks + + if (! -e "$destdir/$basename" && !-l "$destdir/$basename") { + install_dir($destdir); + install_file($page,$destdir.$instname); + } + } + } + + # Now the .so conversion. + @sofiles=@sodests=(); + foreach my $dir (qw{usr/share/man}) { + if (-e "$tmp/$dir") { + find(\&find_so_man, "$tmp/$dir"); + } + } + foreach my $sofile (@sofiles) { + my $sodest=shift(@sodests); + doit "rm","-f",$sofile; + doit "ln","-sf",$sodest,$sofile; + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installmenu b/dh_installmenu new file mode 100755 index 0000000..c30e90f --- /dev/null +++ b/dh_installmenu @@ -0,0 +1,100 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installmenu - install Debian menu files into package build directories + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installmenu> [S<B<debhelper options>>] [B<-n>] + +=head1 DESCRIPTION + +B<dh_installmenu> is a debhelper program that is responsible for installing +files used by the Debian B<menu> package into package build directories. + +It also automatically generates the F<postinst> and F<postrm> commands needed to +interface with the Debian B<menu> package. These commands are inserted into +the maintainer scripts by L<dh_installdeb(1)>. + +=head1 FILES + +=over 4 + +=item debian/I<package>.menu + +Debian menu files, installed into usr/share/menu/I<package> in the package +build directory. See L<menufile(5)> for its format. + +=item debian/I<package>.menu-method + +Debian menu method files, installed into etc/menu-methods/I<package> +in the package build directory. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-n>, B<--no-scripts> + +Do not modify F<postinst>/F<postrm> scripts. + +=back + +=cut + +init(); + +# PROMISE: DH NOOP WITHOUT menu menu-method cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + my $menu=pkgfile($package,"menu"); + my $menu_method=pkgfile($package,"menu-method"); + + if ($menu ne '') { + install_dir("$tmp/usr/share/menu"); + install_file($menu,"$tmp/usr/share/menu/$package"); + + # Add the scripts if a menu-method file doesn't exist. + # The scripts for menu-method handle everything these do, too. + if ($menu_method eq "" && ! $dh{NOSCRIPTS}) { + autoscript($package,"postinst","postinst-menu"); + autoscript($package,"postrm","postrm-menu") + } + } + + if ($menu_method ne '') { + install_dir("$tmp/etc/menu-methods"); + install_file($menu_method,"$tmp/etc/menu-methods/$package"); + + if (! $dh{NOSCRIPTS}) { + autoscript($package, 'postinst', 'postinst-menu-method', { 'PACKAGE' => $package }); + autoscript($package, 'postrm', 'postrm-menu-method', { 'PACKAGE' => $package }); + } + } +} + +=head1 SEE ALSO + +L<debhelper(7)> +L<update-menus(1)> +L<menufile(5)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installmime b/dh_installmime new file mode 100755 index 0000000..5ad6190 --- /dev/null +++ b/dh_installmime @@ -0,0 +1,73 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installmime - install mime files into package build directories + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installmime> [S<I<debhelper options>>] + +=head1 DESCRIPTION + +B<dh_installmime> is a debhelper program that is responsible for installing +mime files into package build directories. + +=head1 FILES + +=over 4 + +=item debian/I<package>.mime + +Installed into usr/lib/mime/packages/I<package> in the package build +directory. + +=item debian/I<package>.sharedmimeinfo + +Installed into /usr/share/mime/packages/I<package>.xml in the package build +directory. + +=back + +=cut + +init(); + +# PROMISE: DH NOOP WITHOUT mime sharedmimeinfo cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + + my $mime=pkgfile($package,"mime"); + if ($mime ne '') { + install_dir("$tmp/usr/lib/mime/packages"); + install_file($mime, "$tmp/usr/lib/mime/packages/$package"); + } + + my $sharedmimeinfo=pkgfile($package,"sharedmimeinfo"); + if ($sharedmimeinfo ne '') { + install_dir("$tmp/usr/share/mime/packages"); + install_file($sharedmimeinfo, + "$tmp/usr/share/mime/packages/$package.xml"); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installmodules b/dh_installmodules new file mode 100755 index 0000000..924a338 --- /dev/null +++ b/dh_installmodules @@ -0,0 +1,119 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installmodules - register kernel modules + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; +use File::Find; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installmodules> [S<I<debhelper options>>] [B<-n>] [B<--name=>I<name>] + +=head1 DESCRIPTION + +B<dh_installmodules> is a debhelper program that is responsible for +registering kernel modules. + +Kernel modules are searched for in the package build directory and if +found, F<preinst>, F<postinst> and F<postrm> commands are automatically generated to +run B<depmod> and register the modules when the package is installed. +These commands are inserted into the maintainer scripts by +L<dh_installdeb(1)>. + +=head1 FILES + +=over 4 + +=item debian/I<package>.modprobe + +Installed to etc/modprobe.d/I<package>.conf in the package build directory. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-n>, B<--no-scripts> + +Do not modify F<preinst>/F<postinst>/F<postrm> scripts. + +=item B<--name=>I<name> + +When this parameter is used, B<dh_installmodules> looks for and +installs files named debian/I<package>.I<name>.modprobe instead +of the usual debian/I<package>.modprobe + +=back + +=head1 NOTES + +Note that this command is not idempotent. L<dh_prep(1)> should be called +between invocations of this command. Otherwise, it may cause multiple +instances of the same text to be added to maintainer scripts. + +=cut + +init(); + +# Looks for kernel modules in the passed directory. If any are found, +# returns the kernel version (or versions) that the modules seem to be for. +sub find_kernel_modules { + my $searchdir=shift; + my %versions; + + return unless -d $searchdir; + find(sub { + if (m/ [.]k?o (?:[.](?:[gx]z|bz2))? $/x) { + my ($kvers)=$File::Find::dir=~m!lib/modules/([^/]+)/!; + if (! defined $kvers || ! length $kvers) { + warning("Cannot determine kernel version for module $File::Find::name"); + } + else { + $versions{$kvers}=1; + } + } + }, $searchdir); + + return sort(keys(%versions)); +} + +# PROMISE: DH NOOP WITHOUT modprobe tmp(lib/modules) cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + my $modprobe_file=pkgfile($package,"modprobe"); + + if ($modprobe_file) { + my $path = '/etc/modprobe.d/' . pkgfilename($package) . '.conf'; + install_dir("$tmp/etc/modprobe.d"); + install_file($modprobe_file, "$tmp/$path"); + } + + if (! $dh{NOSCRIPTS}) { + foreach my $kvers (find_kernel_modules("$tmp/lib/modules")) { + autoscript($package, 'postinst', 'postinst-modules', { 'KVERS' => $kvers }); + autoscript($package, 'postrm', 'postrm-modules', { 'KVERS' => $kvers }); + } + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installpam b/dh_installpam new file mode 100755 index 0000000..193e334 --- /dev/null +++ b/dh_installpam @@ -0,0 +1,81 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installpam - install pam support files + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installpam> [S<I<debhelper options>>] [B<--name=>I<name>] + +=head1 DESCRIPTION + +B<dh_installpam> is a debhelper program that is responsible for installing +files used by PAM into package build directories. + +=head1 FILES + +=over 4 + +=item debian/I<package>.pam + +Installed into usr/lib/pam.d/I<package> in the package build directory. + +Until compatibility level 14 this file was installed under +etc/pam.d/I<package>. Please consider using the "rm_conffile" feature from +L<dh_installdeb(1)> to ensure the proper removal of previous PAM files. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<--name=>I<name> + +Look for files named debian/I<package>.I<name>.pam and install them as +usr/lib/pam.d/I<name>, instead of using the usual files and installing them +using the package name. + +=back + +=cut + +init(); + +# PROMISE: DH NOOP WITHOUT pam cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + my $pam=pkgfile($package,"pam"); + + my $pamd_dir="/usr/lib/pam.d"; + if (compat(13)) { + $pamd_dir="/etc/pam.d"; + } + + if ($pam ne '') { + install_dir("$tmp/$pamd_dir"); + install_file($pam,"$tmp/$pamd_dir/".pkgfilename($package)); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installppp b/dh_installppp new file mode 100755 index 0000000..68784a6 --- /dev/null +++ b/dh_installppp @@ -0,0 +1,78 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installppp - install ppp ip-up and ip-down files + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installppp> [S<I<debhelper options>>] [B<--name=>I<name>] + +=head1 DESCRIPTION + +B<dh_installppp> is a debhelper program that is responsible for installing +ppp ip-up and ip-down scripts into package build directories. + +=head1 FILES + +=over 4 + +=item debian/I<package>.ppp.ip-up + +Installed into etc/ppp/ip-up.d/I<package> in the package build directory. + +=item debian/I<package>.ppp.ip-down + +Installed into etc/ppp/ip-down.d/I<package> in the package build directory. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<--name=>I<name> + +Look for files named F<debian/package.name.ppp.ip-*> and install them as +F<etc/ppp/ip-*/name>, instead of using the usual files and installing them +as the package name. + +=back + +=cut + +init(); + +# PROMISE: DH NOOP WITHOUT ppp.ip-up ppp.ip-down cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + + foreach my $script (qw(up down)) { + my $file=pkgfile($package, "ppp.ip-$script"); + if ($file ne '') { + install_dir("$tmp/etc/ppp/ip-$script.d"); + install_prog($file,"$tmp/etc/ppp/ip-$script.d/".pkgfilename($package)); + } + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installsystemd b/dh_installsystemd new file mode 100755 index 0000000..aba9d78 --- /dev/null +++ b/dh_installsystemd @@ -0,0 +1,453 @@ +#!/usr/bin/perl -w + +=head1 NAME + +dh_installsystemd - install systemd unit files + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; +use File::Find; +use Cwd qw(getcwd abs_path); + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installsystemd> [S<I<debhelper options>>] [B<--restart-after-upgrade>] [B<--no-stop-on-upgrade>] [B<--no-enable>] [B<--no-start>] [B<--name=>I<name>] [S<I<unit file> ...>] + +=head1 DESCRIPTION + +B<dh_installsystemd> is a debhelper program that is responsible for +installing package maintainer supplied systemd unit files. + +It also finds the service files installed by a package and generates +F<preinst>, F<postinst>, and F<prerm> code blocks for enabling, +disabling, starting, stopping, and restarting the corresponding +systemd services, when the package is installed, updated, or +removed. These snippets are added to the maintainer scripts by +L<dh_installdeb(1)>. + +L<deb-systemd-helper(1)> is used to enable and disable systemd units, +thus it is not necessary that the machine actually runs systemd during +package installation time, enabling happens on all machines in order +to be able to switch from sysvinit to systemd and back. + +B<dh_installsystemd> operates on all unit files installed by a +package. For only generating blocks for specific unit files, pass them +as arguments, C<dh_installsystemd quota.service>. Specific unit files +can be excluded from processing using the B<-X> common L<debhelper(1)> +option. + +=head1 FILES + +=over 4 + +=item debian/I<package>.mount, + debian/I<package>.path, + debian/I<package>@.path, + debian/I<package>.service, + debian/I<package>@.service, + debian/I<package>.socket, + debian/I<package>@.socket, + debian/I<package>.target, + debian/I<package>@.target, + debian/I<package>.timer, + debian/I<package>@.timer + +If any of those files exists, they are installed into +F<lib/systemd/system/> in the package build directory. + +=item debian/I<package>.tmpfile + +Only used in compat 12 or earlier. In compat 13+, this file is +handled by L<dh_installtmpfiles(1)> instead. + +If this exists, it is installed into F<usr/lib/tmpfiles.d/> in the +package build directory. Note that the C<tmpfiles.d> mechanism is +currently only used by systemd. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<--no-enable> + +Disable the service(s) on purge, but do not enable them on install. + +B<Note> that this option does not affect whether the services are +started. Please remember to also use B<--no-start> if the service +should not be started. + +=item B<--name=>I<name> + +This option controls several things. + +It changes the name that B<dh_installsystemd> uses when it looks for +maintainer provided systemd unit files as listed in the L</FILES> +section. As an example, B<dh_installsystemd --name foo> will look for +F<<< I<debian/package.>B<< I<foo> >>I<.service> >>> instead of +F<< I<debian/package.service> >>). These unit files are installed as F<< +I<name.unit-extension> >> (in the example, it would be installed as +F<<< B<< I<foo> >>I<.service> >>>). + +Furthermore, if no unit files are passed explicitly as command line +arguments, B<dh_installsystemd> will only act on unit files called +I<name> (rather than all unit files found in the package). + +=item B<--restart-after-upgrade> + +Do not stop the unit file until after the package upgrade has been completed. +This is the default behaviour in compat 10. + +In earlier compat levels the default was to stop the unit file in the +F<prerm>, and start it again in the F<postinst>. + +This can be useful for daemons that should not have a possibly long +downtime during upgrade. But you should make sure that the daemon will not +get confused by the package being upgraded while it's running before using +this option. + +=item B<--no-restart-after-upgrade> + +Undo a previous B<--restart-after-upgrade> (or the default of compat +10). If no other options are given, this will cause the service to be +stopped in the F<prerm> script and started again in the F<postinst> +script. + +=item B<-r>, B<--no-stop-on-upgrade>, B<--no-restart-on-upgrade> + +Do not stop service on upgrade. This has the side-effect of not +restarting the service as a part of the upgrade. + +If you want to restart the service with minimal downtime, please use +B<--restart-after-upgrade> (default in compat 10 or later). If you want +the service to be restarted but be stopped during the upgrade, then please +use B<--no-restart-after-upgrade> (note the "after-upgrade"). + +Note that the B<--no-restart-on-upgrade> alias is deprecated and will +be removed in compat 14. This is to avoid confusion with the +B<--no-restart-after-upgrade> option. + +=item B<--no-start> + +Do not start the unit file after upgrades and after initial installation (the +latter is only relevant for services without a corresponding init script). + +B<Note> that this option does not affect whether the services are +enabled. Please remember to also use B<--no-enable> if the services +should not be enabled. + +=item S<B<unit file> ...> + +Only process and generate maintscripts for the installed unit files +with the (base)name I<unit file>. + +Note: B<dh_installsystemd> will still install unit files from +F<debian/> but it will not generate any maintscripts for them unless +they are explicitly listed in S<B<unit file> ...> + +=back + +=head1 NOTES + +This command is not idempotent. L<dh_prep(1)> should be called between +invocations of this command (with the same arguments). Otherwise, it +may cause multiple instances of the same text to be added to +maintainer scripts. + +=cut + +$dh{RESTART_AFTER_UPGRADE} = ''; +$dh{NO_START} = ''; + +init(options => { + "no-enable" => \$dh{NO_ENABLE}, + "r" => \$dh{R_FLAG}, + 'no-stop-on-upgrade' => \$dh{R_FLAG}, + "no-restart-on-upgrade" => \$dh{R_FLAG}, + "no-start" => \$dh{NO_START}, + "R|restart-after-upgrade!" => \$dh{RESTART_AFTER_UPGRADE}, + "no-also" => \$dh{NO_ALSO}, # undocumented option +}); + +if ($dh{RESTART_AFTER_UPGRADE} eq '') { + $dh{RESTART_AFTER_UPGRADE} = 1 if not defined($dh{R_FLAG}) and $dh{NO_START} eq ''; +} + +sub quote { + # Add single quotes around the argument. + return '\'' . $_[0] . '\''; +} + +sub uniq { + my %seen; + return grep { !$seen{$_}++ } @_; +} + +sub contains_install_section { + my ($unit_path) = @_; + + open(my $fh, '<', $unit_path) or error("Cannot open($unit_path): $!"); + + while (my $line = <$fh>) { + chomp($line); + return 1 if $line =~ /^\s*\[Install\]$/i; + } + close($fh); + return 0; +} + +sub has_sysv_equivalent { + my ($tmpdir, $unit) = @_; + + $unit =~ s/\.(?:mount|service|socket|target|path)$//g; + return -f "$tmpdir/etc/init.d/$unit"; +} + +sub install_unit { + my ($package, $script, $pkgsuffix, $path, $installsuffix) = @_; + $installsuffix = $installsuffix || $pkgsuffix; + my $unit = pkgfile($package, $pkgsuffix); + return if $unit eq ''; + install_dir($path); + install_file($unit, "${path}/${script}.${installsuffix}"); +} + +# Extracts the directive values from a unit file. Handles repeated +# directives in the same unit file. Assumes values can only be +# composed of lists of unit names. This is good enough for the 'Also=' +# and 'Alias=' directives handled here. +sub extract_key { + my ($unit_path, $key) = @_; + my @values; + + open(my $fh, '<', $unit_path) or error("Cannot open($unit_path): $!"); + + while (my $line = <$fh>) { + chomp($line); + + # Since unit names can't have whitespace in systemd, simply + # use split and strip any leading/trailing quotes. See + # systemd-escape(1) for examples of valid unit names. + if ($line =~ /^\s*$key=(.+)$/i) { + for my $value (split(/\s+/, $1)) { + $value =~ s/^(["'])(.*)\g1$/$2/; + push @values, $value; + } + } + } + + close($fh); + return @values; +} + +sub list_installed_units { + my ($tmpdir, $aliases) = @_; + + my $lib_systemd_system = "$tmpdir/lib/systemd/system"; + my @installed; + + return unless -d $lib_systemd_system; + opendir(my $dh, "$lib_systemd_system") or error("Cannot opendir($lib_systemd_system): $!"); + + foreach my $name (readdir($dh)) { + my $path = "$lib_systemd_system/$name"; + next unless -f $path; + if (-l "$path") { + my $dest = basename(readlink($path)); + $aliases->{$dest} //= [ ]; + push @{$aliases->{$dest}}, $name; + } else { + push @installed, $name; + } + } + + closedir($dh); + return @installed; +} + + +# PROMISE: DH NOOP WITHOUT internal(bug#950723) tmp(lib/systemd/system) tmp(usr/lib/tmpfiles.d) tmp(etc/tmpfiles.d) mount path service socket target tmpfile timer cli-options() + + +# Install package maintainer supplied unit files +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmpdir = tmpdir($package); + + # Intall all unit files in the debian/ directory with names in the + # form $package.(service|target|socket|path|timer|mount|tmpfile) + # and their templated version when relevant. + + # This can be modified with the --name option to look for unit + # files with names in the form $package.$name.(service|...) and + # $name.(service|target|socket|path|timer|mount|tmpfile) and their + # templated version when relevant. + my $name = $dh{NAME} // $package; + + for my $type (qw(service target socket path timer)) { + install_unit($package, $name, $type, "$tmpdir/lib/systemd/system"); + install_unit("${package}@", "${name}@", $type, "$tmpdir/lib/systemd/system"); + } + + install_unit($package, $name, 'mount', "$tmpdir/lib/systemd/system"); + # In compat 13+, this is handled by dh_installtmpfiles + install_unit($package, $name, 'tmpfile', "$tmpdir/usr/lib/tmpfiles.d", 'conf') if compat(12); +} + + +if (compat(12)) { + # In compat 13+, this is handled by dh_installtmpfiles + # Add postinst code blocks to handle tmpfiles + foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmpdir = tmpdir($package); + my @tmpfiles; + + my @dirs = grep { -d } map { "${tmpdir}/$_" } qw(usr/lib/tmpfiles.d etc/tmpfiles.d); + + find({ + wanted => sub { + my $name = $File::Find::name; + return if not -f $name or not $name =~ m{[.]conf$}; + push(@tmpfiles, basename($name)); }, + no_chdir => 1, + }, @dirs) if @dirs; + + if (@tmpfiles) { + autoscript($package, 'postinst', 'postinst-init-tmpfiles', { 'TMPFILES' => join(' ', sort @tmpfiles) }); + } + } +} + + +# Add postinst, prerm, and postrm code blocks to handle activation, +# deactivation, start and stopping of services when the package is +# installed, upgraded or removed. +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmpdir = tmpdir($package); + my (@args, @start_units, @enable_units, %aliases); + + my @installed_units = list_installed_units($tmpdir, \%aliases); + + # Handle either only the unit files which were passed as arguments + # or all unit files that are installed in this package. + if (@ARGV) { + @args = @ARGV; + } + elsif ($dh{NAME}) { + # Treat --name option as if the corresponding unit names were + # passed in the command line. + @args = grep /(^|\/)$dh{NAME}\.(mount|path|service|socket|target|timer)$/, @installed_units; + } + else { + @args = @installed_units; + } + + # Support excluding units via the -X debhelper common option. + foreach my $x (@{$dh{EXCLUDE}}) { + @args = grep !/(^|\/)$x$/, @args; + } + + # This hash prevents us from looping forever in the following + # while loop. An actual real-world example of such a loop is + # systemd's systemd-readahead-drop.service, which contains + # Also=systemd-readahead-collect.service, and that file in turn + # contains Also=systemd-readahead-drop.service, thus forming an + # endless loop. + my %seen; + + # Must use while and shift because the loop alters the list. + while (@args) { + my $unit = shift @args; + my $path = "${tmpdir}/lib/systemd/system/${unit}"; + + error("Package '$package' does not install unit '$unit'.") unless (-f $path); + + # Skip template service files. Enabling, disabling, starting + # or stopping those services without specifying the instance + # is not useful. + next if ($unit =~ /\@/); + + # Handle all unit files specified via Also= explicitly. This + # is not necessary for enabling, but for disabling, as we + # cannot read the unit file when disabling as it has already + # been deleted. The undocumented --no-also option disables + # handling of units linked via Also=. This option is provided + # only to suport a very specific use case in network-manager. + unless ($dh{NO_ALSO}) { + push @args, $_ for grep { !$seen{$_}++ } extract_key($path, 'Also'); + } + + # Extract unit aliases. + push @{$aliases{$unit}}, $_ for extract_key($path, 'Alias'); + + # In compat 11 (and earlier), dh_installinit will handle services with + # a sysv-equivalent service. In compat 12, dh_installsystemd will + # take care of it. + if (not compat(11) or not grep { has_sysv_equivalent($tmpdir, $_) } ($unit, @{$aliases{$unit}})) { + push @start_units, $unit; + } + + if (contains_install_section($path)) { + push @enable_units, $unit; + } + } + + @enable_units = map { quote($_) } uniq sort @enable_units; + @start_units = map { quote($_) } uniq sort @start_units; + + my %options = ('snippet-order' => 'service'); + + if (@enable_units) { + for my $unit (@enable_units) { + my $snippet = $dh{NO_ENABLE} ? 'postinst-systemd-dont-enable' : 'postinst-systemd-enable'; + autoscript($package, 'postinst', $snippet, { 'UNITFILE' => $unit }, \%options); + } + autoscript($package, 'postrm', 'postrm-systemd', {'UNITFILES' => join(' ', @enable_units) }); + } + + if (@start_units) { + my $replace = { 'UNITFILES' => join(' ', @start_units) }; + + if ($dh{RESTART_AFTER_UPGRADE}) { + my $snippet; + if ($dh{NO_START}) { + $snippet = 'postinst-systemd-restartnostart'; + $replace->{RESTART_ACTION} = 'try-restart'; + } else { + $snippet = 'postinst-systemd-restart'; + $replace->{RESTART_ACTION} = 'restart'; + } + autoscript($package, 'postinst', $snippet, $replace, \%options); + } elsif (!$dh{NO_START}) { + # (stop|start) service (before|after) upgrade + autoscript($package, 'postinst', 'postinst-systemd-start', $replace, \%options); + } + + # stop service before upgrade, if requested + autoscript($package, 'preinst', 'preinst-systemd-stop', $replace, \%options) + unless ($dh{R_FLAG} || $dh{RESTART_AFTER_UPGRADE}); + + # stop service only on remove + autoscript($package, 'prerm', 'prerm-systemd-restart', $replace, \%options) + unless ($dh{NO_START}); + + # Run this with "default" order so it is always after other + # service related autosnippets. + autoscript($package, 'postrm', 'postrm-systemd-reload-only', $replace); + } +} + +=head1 SEE ALSO + +L<debhelper(7)>, L<dh_installinit(1)>, L<deb-systemd-helper(1)> + +=head1 AUTHORS + +pkg-systemd-maintainers@lists.alioth.debian.org + +=cut diff --git a/dh_installsystemduser b/dh_installsystemduser new file mode 100755 index 0000000..7ab7933 --- /dev/null +++ b/dh_installsystemduser @@ -0,0 +1,288 @@ +#!/usr/bin/perl -w + +=head1 NAME + +dh_installsystemduser - install systemd unit files + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installsystemduser> [S<I<debhelper options>>] [B<--no-enable>] [B<--name=>I<name>] [S<I<unit file> ...>] + +=head1 DESCRIPTION + +B<dh_installsystemduser> finds the systemd user instance service files +installed by a package and generates F<preinst>, F<postinst>, and F<prerm> +code blocks for enabling, disabling, starting, stopping, and restarting the +corresponding systemd user instance services, when the package is installed, +updated, or removed. These snippets are added to the maintainer scripts by +L<dh_installdeb(1)>. + +L<deb-systemd-helper(1)> is used to enable and disable the systemd +units, thus it is not necessary that the machine actually runs systemd +during package installation time, enabling happens on all machines. + +B<dh_installsystemduser> operates on all user instance unit files +installed by a package. For only generating blocks for specific unit +files, pass them as arguments. Specific unit files can be excluded +from processing using the B<-X> common L<debhelper(1)> option. + +=head1 FILES + +=over 4 + +=item debian/I<package>.user.path, + debian/I<package>@.user.path, + debian/I<package>.user.service, + debian/I<package>@.user.service, + debian/I<package>.user.socket, + debian/I<package>@.user.socket, + debian/I<package>.user.target, + debian/I<package>@.user.target, + debian/I<package>.user.timer, + debian/I<package>@.user.timer + +If any of those files exists, they are installed into +F<usr/lib/systemd/user/> in the package build directory removing the +F<.user> file name part. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<--name=>I<name> + +Install the service file as I<name.service> instead of the default +filename I<package.service>. When this parameter is used, +B<dh_installsystemd> looks for and installs files named +F<debian/package.name.user.service> instead of the usual +F<debian/package.user.service>. Moreover, maintainer scripts are only +generated for units that match the given I<name>. + +=item B<--no-enable> + +Disable the service(s) on purge, but do not enable them on install. + +=back + +=head1 NOTES + +This command is not idempotent. L<dh_prep(1)> should be called between +invocations of this command (with the same arguments). Otherwise, it +may cause multiple instances of the same text to be added to +maintainer scripts. + +=cut + +# PROMISE: DH NOOP WITHOUT internal(bug#950723) tmp(usr/lib/systemd/user) user.service + +init(options => { + "no-enable" => \$dh{NO_ENABLE}, +}); + +sub quote { + # Add single quotes around the argument. + return '\'' . $_[0] . '\''; +} + +sub uniq { + my %seen; + return grep { !$seen{$_}++ } @_; +} + +sub contains_install_section { + my ($unit_path) = @_; + + open(my $fh, '<', $unit_path) or error("Cannot open($unit_path) to check for [Install]: $!"); + + while (my $line = <$fh>) { + chomp($line); + return 1 if $line =~ /^\s*\[Install\]$/i; + } + close($fh); + return 0; +} + +sub install_user_unit { + my ($package, $name, $suffix, $path) = @_; + my $unit = pkgfile($package, "user.$suffix"); + return if $unit eq ''; + + install_dir($path); + install_file($unit, "$path/$name.$suffix"); +} + +# Extracts the directive values from a unit file. Handles repeated +# directives in the same unit file. Assumes values can only be +# composed of lists of unit names. This is good enough for the 'Also=' +# and 'Alias=' directives handled here. +sub extract_key { + my ($unit_path, $key) = @_; + my @values; + + open(my $fh, '<', $unit_path) or error("Cannot open($unit_path): $!"); + + while (my $line = <$fh>) { + chomp($line); + + # Since unit names can't have whitespace in systemd, simply + # use split and strip any leading/trailing quotes. See + # systemd-escape(1) for examples of valid unit names. + if ($line =~ /^\s*$key=(.+)$/i) { + for my $value (split(/\s+/, $1)) { + $value =~ s/^(["'])(.*)\g1$/$2/; + push @values, $value; + } + } + } + + close($fh); + return @values; +} + +sub list_installed_user_units { + my ($tmpdir, $aliases) = @_; + + my $lib_systemd_user = "$tmpdir/usr/lib/systemd/user"; + my @installed; + + return unless -d $lib_systemd_user; + opendir(my $dh, $lib_systemd_user) or error("Cannot opendir($lib_systemd_user): $!"); + + foreach my $name (readdir($dh)) { + my $path = "$lib_systemd_user/$name"; + next unless -f $path; + if (-l $path) { + my $dest = basename(readlink($path)); + $aliases->{$dest} //= [ ]; + push @{$aliases->{$dest}}, $name; + } else { + push @installed, $name; + } + } + + closedir($dh); + return @installed; +} + +# Install package maintainer provided unit files. +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmpdir = tmpdir($package); + + # unit file name + my $name = $dh{NAME} // $package; + + my $path = "$tmpdir/usr/lib/systemd/user"; + for my $type (qw(service target socket path timer)) { + install_user_unit($package, $name, $type, $path); + install_user_unit("${package}@", "${name}@", $type, $path); + } +} + +# Generate postinst and prerm code blocks to enable and disable units +foreach my $package (@{$dh{DOPACKAGES}}) { + my (@args, @start_units, @enable_units, %aliases); + + my $tmpdir = tmpdir($package); + my @units = list_installed_user_units($tmpdir, \%aliases); + + # Handle either only the unit files which were passed as arguments + # or all unit files that are installed in this package. + if (@ARGV) { + @args = @ARGV; + } + elsif ($dh{NAME}) { + # Treat --name flag as if the corresponding units were passed + # in the command line. + @args = grep /(^|\/)$dh{NAME}\.(service|target|socket|path|timer)$/, @units; + } + else { + @args = @units; + } + + # Support excluding units via the -X debhelper common option. + foreach my $x (@{$dh{EXCLUDE}}) { + @args = grep !/(^|\/)$x$/, @args; + } + + # This hash prevents us from looping forever in the following + # while loop. An actual real-world example of such a loop is + # systemd's systemd-readahead-drop.service, which contains + # Also=systemd-readahead-collect.service, and that file in turn + # contains Also=systemd-readahead-drop.service, thus forming an + # endless loop. + my %seen; + + # Must use while and shift because the loop alters the list. + while (@args) { + my $name = shift @args; + my $path = "${tmpdir}/usr/lib/systemd/user/${name}"; + + error("User unit file \"$name\" not found in package \"$package\".") if ! -f $path; + + # Skip template service files. Enabling or disabling those + # services without specifying the instance is not useful. + next if ($name =~ /\@/); + + # Handle all unit files specified via Also= explicitly. This + # is not necessary for enabling, but for disabling, as we + # cannot read the unit file when disabling as it has already + # been deleted. + push @args, $_ for grep { !$seen{$_}++ } extract_key($path, 'Also'); + + push @enable_units, $name if contains_install_section($path); + push @start_units, $name; + } + + @enable_units = map { quote($_) } sort(uniq(@enable_units)); + @start_units = map { quote($_) } sort(uniq(@start_units)); + + if (@enable_units) { + # The generated maintainer script code blocks use the --user + # option that was added to deb-systemd-helper in version 1.52. + addsubstvar($package, 'misc:Depends', 'init-system-helpers', ">= 1.52"); + + my $postinst = $dh{NO_ENABLE} ? 'postinst-systemd-user-dont-enable' : 'postinst-systemd-user-enable'; + foreach my $unit (@enable_units) { + autoscript($package, 'postinst', $postinst, { 'UNITFILE' => $unit }); + } + autoscript($package, 'postrm', 'postrm-systemd-user', { 'UNITFILES' => join(' ', @enable_units) }); + } + + if (@start_units and not compat(13)) { + # The generated maintainer script code blocks use the --user + # option that was added to deb-systemd-invoke in version 1.61. + addsubstvar($package, 'misc:Depends', 'init-system-helpers', ">= 1.61~"); + + my %options = ('snippet-order' => 'service'); + + # restart service after install/upgrade + autoscript($package, 'postinst', 'postinst-systemd-user-restart', { 'UNITFILES' => join(' ', @start_units) }, \%options); + + # stop service after removal + autoscript($package, 'prerm', 'prerm-systemd-user-stop', { 'UNITFILES' => join(' ', @start_units) }, \%options); + + # Run this with "default" order so it is always after other + # service related autosnippets. + autoscript($package, 'postrm', 'postrm-systemd-user-reload-only', { 'UNITFILES' => join(' ', @start_units) }); + } +} + +=head1 SEE ALSO + +L<debhelper(7)>, L<dh_installsystemd(1)>, L<deb-systemd-helper(1)> + +=head1 AUTHORS + +pkg-systemd-maintainers@lists.alioth.debian.org + +=cut diff --git a/dh_installsysusers b/dh_installsysusers new file mode 100755 index 0000000..faa4b8e --- /dev/null +++ b/dh_installsysusers @@ -0,0 +1,115 @@ +#!/usr/bin/perl -w + +=head1 NAME + +dh_installsysusers - install and integrates systemd sysusers files + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installsysusers> [S<I<debhelper options>>] + +=head1 DESCRIPTION + +B<dh_installsysusers> is a debhelper program that is responsible for +installing package maintainer supplied systemd sysusers files. + +It also finds the systemd sysusers files installed in a package and +generates relevant integration snippets for enabling the users on +installation. These snippets are added to the package by +L<dh_installdeb(1)>. + +=head1 FILES + +=over 4 + +=item debian/I<package>.sysusers + +If the file exist, it will be installed as +F<< /usr/lib/sysusers.d/I<package>.conf >>. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<--name=>I<name> + +When this parameter is used, B<dh_installsysusers> looks for and +installs files named debian/I<package>.I<name>.sysusers instead +of the usual debian/I<package>.sysusers. + +Furthermore, the file is installed as F<< /usr/lib/sysusers.d/I<name>.conf >> +rather than F<< /usr/lib/sysusers.d/I<package>.conf >>. + +=back + +=head1 NOTES + +This command is not idempotent. L<dh_prep(1)> should be called between +invocations of this command (with the same arguments). Otherwise, it +may cause multiple instances of the same text to be added to +maintainer scripts. + +=cut + +init(); + + +# PROMISE: DH NOOP WITHOUT pkgfile(sysusers) tmp(usr/lib/sysusers.d) cli-options() + + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmpdir = tmpdir($package); + my $sysusers = pkgfile($package,"sysusers"); + my $sysusers_targetdir = "${tmpdir}/usr/lib/sysusers.d"; + my $target = $dh{NAME} // $package; + my $typoed_name = pkgfile($package, "sysuser"); + + if ($sysusers eq '' and $typoed_name ne '') { + # Warn people in case they typo this as much as I did. + my $correct_name = $typoed_name; + $correct_name =~ s{^(?:.*[./])\Ksysuser}{sysusers}; + warning("Possible typo in ${typoed_name} (expected ${correct_name}): File has been ignored"); + } + + if ($sysusers ne '') { + install_dir($sysusers_targetdir); + install_file($sysusers, "${sysusers_targetdir}/${target}.conf"); + } + + if (! $dh{NOSCRIPTS} && ($sysusers ne '' || -d $sysusers_targetdir)) { + my @sysusers_files; + opendir(my $dir_fd, $sysusers_targetdir) or error("opendir(${sysusers_targetdir}) failed: $!"); + while (defined(my $entry = readdir($dir_fd))) { + next if $entry eq '.' or $entry eq '..' or $entry !~ m{[.]conf$}; + push @sysusers_files, $entry; + } + closedir($dir_fd); + + next if @sysusers_files == 0; + + # Sort list of files so postinst content doesn't change if readdir's output is not stable + @sysusers_files = sort @sysusers_files; + # Generate a single systemd-sysusers invocation and just pass all detected files together + autoscript($package, 'postinst', 'postinst-sysusers', { 'CONFILE_BASENAME' => "@sysusers_files" }); + addsubstvar($package, "misc:Depends", "systemd | systemd-standalone-sysusers | systemd-sysusers"); + } + +} + + + +=head1 SEE ALSO + +L<debhelper(7)> + +=cut diff --git a/dh_installtmpfiles b/dh_installtmpfiles new file mode 100755 index 0000000..aa5ab72 --- /dev/null +++ b/dh_installtmpfiles @@ -0,0 +1,128 @@ +#!/usr/bin/perl -w + +=head1 NAME + +dh_installtmpfiles - install tmpfiles.d configuration files + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; +use File::Find; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installtmpfiles> [S<I<debhelper options>>][B<--name=>I<name>] + +=head1 DESCRIPTION + +B<dh_installtmpfiles> is a debhelper program that is responsible for +installing package maintainer supplied tmpfiles.d configuration files +(e.g. for systemd-tmpfiles). + +It also finds the tmpfiles.d configuration files installed by a package +and generates F<postinst> code blocks for activating the tmpfiles.d +configuration when the package is installed. These snippets are added +to the maintainer scripts by L<dh_installdeb(1)>. + + +=head1 OPTIONS + +=over 4 + +=item B<--name=>I<name> + +This option controls both a prefix used for lookng up maintainer provided +tmpfiles.d configuration files (those mentioned in the L</FILES> section) +and also the base name used for the installed version of the file. + +=back + +=head1 FILES + +=over 4 + +=item debian/I<package>.tmpfiles + +If this exists, it is installed into F<usr/lib/tmpfiles.d/> in the +package build directory. Note that the C<tmpfiles.d> mechanism is +currently only used by systemd. + +=item debian/I<package>.tmpfile + +Deprecated name for debian/I<package>.tmpfiles. + +=back + +=head1 NOTES + +This command is not idempotent. L<dh_prep(1)> should be called between +invocations of this command (with the same arguments). Otherwise, it +may cause multiple instances of the same text to be added to +maintainer scripts. + +=cut + +init(); + +sub uniq { + my %seen; + return grep { !$seen{$_}++ } @_; +} + +# PROMISE: DH NOOP WITHOUT tmp(usr/lib/tmpfiles.d) tmp(etc/tmpfiles.d) pkgfile(tmpfiles) pkgfile(tmpfile) cli-options() + +# Install package maintainer supplied tmpfiles files +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmpdir = tmpdir($package); + my $tmpfile = pkgfile($package, 'tmpfiles'); + my $name = $dh{NAME} // $package; + my $old_tmpfile = pkgfile($package, 'tmpfile'); + my $dir; + if (not $tmpfile) { + my $new_name; + next if not $old_tmpfile; + $tmpfile = $old_tmpfile; + $new_name = $old_tmpfile; + $new_name =~ s{^(.+[./])tmpfile(\..+|)$}{$1tmpfiles$2}; + warning("The name $tmpfile is deprecated; please use $new_name instead"); + warning(qq{Possible fix: mv -f "${tmpfile}" "${new_name}"}); + } elsif ($old_tmpfile) { + warning("There is both a $tmpfile and a $old_tmpfile that is relevant for this package!?"); + warning(qq{Possible fix: rm -f "${old_tmpfile}"}); + error("Aborting; Please resolve the ambiguity between ${tmpfile} and ${old_tmpfile}."); + } + + $dir = "$tmpdir/usr/lib/tmpfiles.d"; + install_dir($dir); + install_file($tmpfile, "${dir}/${name}.conf"); +} + +# Add postinst code blocks to handle tmpfiles +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmpdir = tmpdir($package); + my @tmpfiles; + + my @dirs = grep { -d } map { "${tmpdir}/$_" } qw(usr/lib/tmpfiles.d etc/tmpfiles.d); + + find({ + wanted => sub { + my $name = $File::Find::name; + return if not -f $name or not $name =~ m{[.]conf$}; + push(@tmpfiles, basename($name)); }, + no_chdir => 1, + }, @dirs) if @dirs; + + if (@tmpfiles) { + autoscript($package, 'postinst', 'postinst-init-tmpfiles', { 'TMPFILES' => join(' ', uniq(sort(@tmpfiles))) }); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +=cut diff --git a/dh_installudev b/dh_installudev new file mode 100755 index 0000000..8e491d1 --- /dev/null +++ b/dh_installudev @@ -0,0 +1,112 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installudev - install udev rules files + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installudev> [S<I<debhelper options>>] [B<-n>] [B<--name=>I<name>] [B<--priority=>I<priority>] + +=head1 DESCRIPTION + +B<dh_installudev> is a debhelper program that is responsible for +installing B<udev> rules files. + +=head1 FILES + +=over 4 + +=item debian/I<package>.udev + +Installed into F<lib/udev/rules.d/> in the package build directory. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<--name=>I<name> + +When this parameter is used, B<dh_installudev> looks for and +installs files named debian/I<package>.I<name>.udev instead of the usual +debian/I<package>.udev. + +=item B<--priority=>I<priority> + +Sets the priority the file. Default is 60. + +=back + +=head1 NOTES + +Note that this command is not idempotent. L<dh_prep(1)> should be called +between invocations of this command. Otherwise, it may cause multiple +instances of the same text to be added to maintainer scripts. + +=cut + +init(options => { + "priority=s" => \$dh{PRIORITY}, +}); + +# The priority used to look like z60_; +# we need to calculate that old value to handle +# conffile moves correctly. +my $old_priority=$dh{PRIORITY}; + +# In case a caller still uses the `z` prefix, remove it. +if (defined $dh{PRIORITY}) { + $dh{PRIORITY}=~s/^z//; +} + +if (! defined $dh{PRIORITY}) { + $dh{PRIORITY}="60"; + $old_priority="z60"; +} +if ($dh{PRIORITY}) { + $dh{PRIORITY}.="-"; + $old_priority.="_"; +} + +# PROMISE: DH NOOP WITHOUT udev cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + my $rules_file=pkgfile($package,"udev"); + my $filename=basename($rules_file); + if ($filename eq 'udev') { + $filename = "$package.udev"; + } + $filename=~s/\.udev$/.rules/; + if (defined $dh{NAME}) { + $filename="$dh{NAME}.rules"; + } + + if ($rules_file) { + my $rule="/lib/udev/rules.d/$dh{PRIORITY}$filename"; + install_dir("$tmp/lib/udev/rules.d"); + install_file($rules_file, "${tmp}${rule}"); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installwm b/dh_installwm new file mode 100755 index 0000000..d5e0599 --- /dev/null +++ b/dh_installwm @@ -0,0 +1,140 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installwm - register a window manager + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installwm> [S<I<debhelper options>>] [B<-n>] [B<--priority=>I<n>] [S<I<wm> ...>] + +=head1 DESCRIPTION + +B<dh_installwm> is a debhelper program that is responsible for +generating the F<postinst> and F<prerm> commands that register a window manager +with L<update-alternatives(8)>. The window manager's man page is also +registered as a slave symlink (in v6 mode and up). It must be installed in +F<usr/share/man/man1/> in the package build directory prior to calling +B<dh_installwm>. In compat 9 and earlier, the manpage was optional. + +=head1 FILES + +=over 4 + +=item debian/I<package>.wm + +List window manager programs to register. + +Supports substitution variables in compat 13 and later as +documented in L<debhelper(7)>. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<--priority=>I<n> + +Set the priority of the window manager. Default is 20, which is too low for +most window managers; see the Debian Policy document for instructions on +calculating the correct value. + +=item B<-n>, B<--no-scripts> + +Do not modify F<postinst>/F<prerm> scripts. Turns this command into a no-op. + +=item B<-A>, B<--all> + +Modify scripts for window managers specified by command line +parameters in ALL packages acted on, not just the first. + +=item I<wm> ... + +Window manager programs to register. + +=back + +=head1 NOTES + +Note that this command is not idempotent. L<dh_prep(1)> should be called +between invocations of this command. Otherwise, it may cause multiple +instances of the same text to be added to maintainer scripts. + +=cut + +init(options => { + "priority=s" => \$dh{PRIORITY}, +}); + +if (! defined $dh{PRIORITY}) { + $dh{PRIORITY}=20; +} + +if (@ARGV) { + # This is here for backwards compatibility. If the filename doesn't + # include a path, assume it's in /usr/bin. + if ($ARGV[0] !~ m:/:) { + $ARGV[0]="/usr/bin/$ARGV[0]"; + } +} + +my $nodocs = is_build_profile_active('nodoc') || get_buildoption('nodoc') ? 1 : 0; + +# PROMISE: DH NOOP WITHOUT wm cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + my $file=pkgfile($package,"wm"); + + my @wm; + if ($file) { + @wm=filearray($file, '.'); + } + + if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) { + push @wm, @ARGV; + } + + if (! $dh{NOSCRIPTS}) { +WM: foreach my $wm (@wm) { + autoscript($package,"prerm","prerm-wm", { 'WM' => $wm }); + + my $wmman; + foreach my $ext (".1", ".1x") { + $wmman="/usr/share/man/man1/".basename($wm).$ext; + if (-e "$tmp$wmman" || -e "$tmp$wmman.gz") { + autoscript($package,"postinst","postinst-wm", { 'WM' => $wm, 'WMMAN' => "${wmman}.gz" , 'PRIORITY' => $dh{PRIORITY} }); + next WM; + } + } + if (not compat(9) and not $nodocs) { + error("no manpage found (creating an x-window-manager alternative requires a slave symlink for the manpage)"); + } else { + warning("no manpage found (creating an x-window-manager alternative requires a slave symlink for the manpage)"); + } + # Reaching this code means a broken package will be produced. + autoscript($package,"postinst","postinst-wm-noman", { 'WM' => $wm, 'PRIORITY' => $dh{PRIORITY} }); + } + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installxfonts b/dh_installxfonts new file mode 100755 index 0000000..c16659f --- /dev/null +++ b/dh_installxfonts @@ -0,0 +1,100 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installxfonts - register X fonts + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installxfonts> [S<I<debhelper options>>] + +=head1 DESCRIPTION + +B<dh_installxfonts> is a debhelper program that is responsible for +registering X fonts, so their corresponding F<fonts.dir>, F<fonts.alias>, +and F<fonts.scale> be rebuilt properly at install time. + +Before calling this program, you should have installed any X fonts provided +by your package into the appropriate location in the package build +directory, and if you have F<fonts.alias> or F<fonts.scale> files, you should +install them into the correct location under F<etc/X11/fonts> in your +package build directory. + +Your package should depend on B<xfonts-utils> so that the +B<update-fonts->I<*> commands are available. (This program adds that dependency to +B<${misc:Depends}>.) + +This program automatically generates the F<postinst> and F<postrm> commands needed +to register X fonts. These commands are inserted into the maintainer +scripts by B<dh_installdeb>. See L<dh_installdeb(1)> for an explanation of how +this works. + +=head1 NOTES + +See L<update-fonts-alias(8)>, L<update-fonts-scale(8)>, and +L<update-fonts-dir(8)> for more information about X font installation. + +See Debian policy, section 11.8.5. for details about doing fonts the Debian +way. + +=cut + +init(); + +# PROMISE: DH NOOP WITHOUT tmp(usr/share/fonts/X11) cli-options() + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmp=tmpdir($package); + + # Find all font directories in the package build directory. + my @fontdirs; + foreach my $parentdir ("$tmp/usr/share/fonts/X11/") { + opendir(DIR, $parentdir) || next; + @fontdirs = grep { -d "$parentdir/$_" && !/^\./ } (readdir DIR); + closedir DIR; + } + + if (@fontdirs) { + # Figure out what commands the postinst and postrm will need + # to call. + my (@cmds, @cmds_postinst, @cmds_postrm); + # Sort items for reproducible binary package contents. + foreach my $f (sort @fontdirs) { + # This must come before update-fonts-dir. + push @cmds, "update-fonts-scale $f" + if -f "$tmp/etc/X11/fonts/$f/$package.scale"; + push @cmds, "update-fonts-dir --x11r7-layout $f"; + if (-f "$tmp/etc/X11/fonts/$f/$package.alias") { + push @cmds_postinst, "update-fonts-alias --include /etc/X11/fonts/$f/$package.alias $f"; + push @cmds_postrm, "update-fonts-alias --exclude /etc/X11/fonts/$f/$package.alias $f"; + } + } + + autoscript($package, "postinst", "postinst-xfonts", + { 'CMDS' => join(";", @cmds, @cmds_postinst) }); + autoscript($package, "postrm", "postrm-xfonts", + { 'CMDS' => join(";", @cmds, @cmds_postrm) }); + + addsubstvar($package, "misc:Depends", "xfonts-utils"); + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut |