From d50611823c584b891ad076ef922cfddd67c51752 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 11 Jun 2013 23:32:11 +0200 Subject: add dh-systemd package, update changelog --- script/dh_systemd | 301 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100755 script/dh_systemd (limited to 'script/dh_systemd') diff --git a/script/dh_systemd b/script/dh_systemd new file mode 100755 index 0000000..e513e14 --- /dev/null +++ b/script/dh_systemd @@ -0,0 +1,301 @@ +#!/usr/bin/perl -w + +=head1 NAME + +dh_systemd - enable/start/stop/restart systemd unit files + +=cut + +use strict; +use Debian::Debhelper::Dh_Lib; +use File::Find; +use Text::ParseWords qw(shellwords); # in core since Perl 5 + +=head1 SYNOPSIS + +B [S>] [B<--no-enable>] [B<--restart-after-upgrade>] [B<--no-restart-on-upgrade>] [B<--assume-sysv-present>] [S ...>] + +=head1 DESCRIPTION + +B is a debhelper program that is responsible for enabling, +starting/stopping or restarting systemd unit files. + +In the simple case, it finds all unit files installed by a package (e.g. +bacula-fd.service) and enables them. 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. + +Furthermore, as with B, the unit file is stopped before +upgrades and started afterwards (unless B<--restart-after-upgrade> is +specified, in which case it will only be restarted after the upgrade). +This logic is not used when there is a corresponding SysV init script +because invoke-rc.d performs the stop/start/restart in that case. + +In the complex case, you can call B manually and specify +flags per unit file. An example is colord, which ships colord.service, a +dbus-activated service without an [Install] section. This service file cannot +be enabled or disabled (a state called "static" by systemd) because it has no +[Install] section. Therefore, run + + dh_systemd --no-enable colord.service + +=head1 OPTIONS + +=over 4 + +=item B<--no-enable> + +Do not enable the unit file. This option is most useful when calling +B for a specific unit file. + +Example (see DESCRIPTION): + dh_systemd --no-enable colord.service + +=item B<--restart-after-upgrade> + +Do not stop the unit file until after the package upgrade has been completed. +This is different than the default behavior, which stops the unit file in the +F and starts it again in the F maintscript. + +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<-r>, B<--no-restart-on-upgrade> + +Do not stop service on upgrade. + +=item B<--assume-sysv-present> + +When running B before B, init scripts might +not be installed yet and thus cannot be found by B. By +specifying B<--assume-sysv-present>, start/stop/restart will be done through +invoke-rc.d, i.e. no systemd-specific code will be generated. + +This option is only useful in cases where the init script is installed with a +different name and you need to run B before B in +order to get aliases (symlinks) created in the scripts before invoke-rc.d is +called. + +=back + +=head1 NOTES + +Note that this command is not idempotent. L 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. + +Note that B should be run after B so that it +can detect corresponding SysV init scripts. The default sequence in B does +the right thing, this note is only relevant when you are calling +B manually. + +=cut + +init(options => { + "r" => \$dh{R_FLAG}, + "no-restart-on-upgrade" => \$dh{R_FLAG}, + "no-start" => \$dh{NO_START}, + "no-enable" => \$dh{NO_ENABLE}, + "R|restart-after-upgrade" => \$dh{RESTART_AFTER_UPGRADE}, + "assume-sysv-present" => \$dh{ASSUME_SYSV_PRESENT}, + "no-also" => \$dh{NO_ALSO}, +}); + +# Extracts the Also= or Alias= line(s) from a unit file. +# In case this produces horribly wrong results, you can pass --no-also, but +# that should really not be necessary. Please report bugs to +# pkg-systemd-maintainers. +sub extract_key { + my ($unit_path, $key) = @_; + my @values; + my $fh; + + if ($dh{NO_ALSO}) { + return @values; + } + + if (!open($fh, '<', $unit_path)) { + warning("Cannot open($unit_path) for extracting the Also= line(s)"); + return; + } + while (my $line = <$fh>) { + chomp($line); + + if ($line =~ /^\s*$key=(.+)$/i) { + @values = (@values, shellwords($1)); + } + } + close($fh); + return @values; +} + +foreach my $package (@{$dh{DOPACKAGES}}) { + my $tmpdir = tmpdir($package); + my @installed_units; + my %unitfiles; + my %aliases; + + find({ + wanted => sub { + my $name = $File::Find::name; + return unless -f $name; + return unless $name =~ m,^$tmpdir/lib/systemd/system/[^/]+$,; + push @installed_units, $name; + }, + no_chdir => 1, + }, $tmpdir); + + # Handle either only the unit files which were passed as arguments or + # all unit files that are installed in this package. + my @args = @ARGV > 0 ? @ARGV : @installed_units; + + # 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; + + # We use while/shift because we push to the list in the body. + while (@args) { + my $name = shift @args; + my $base = basename($name); + + # Try to make the path absolute, so that the user can call + # dh_installsystemd bacula-fd.service + if ($base eq $name) { + # NB: This works because @installed_units contains + # files from precisely one directory. + my ($full) = grep { basename($_) eq $base } @installed_units; + if (defined($full)) { + $name = $full; + } else { + warning(qq|Could not find "$name" in the /lib/systemd/system of $package.| . + qq|This could be a typo, or using Also= with a service file from another package.| . + qq|Please check carefully that this message is harmless.|); + } + } + + # Skip template service files like e.g. getty@.service. + # Enabling, disabling, starting or stopping those services + # without specifying the instance (e.g. getty@ttyS0.service) is + # not useful. + if ($name =~ /\@/) { + return; + } + + # 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 (it was already + # deleted). + my @also = grep { !exists($seen{$_}) } extract_key($name, 'Also'); + $seen{$_} = 1 for @also; + @args = (@args, @also); + + $aliases{$name} = [ extract_key($name, 'Alias') ]; + my @sysv = grep { + my $base = $_; + $base =~ s/\.service$//g; + -f "$tmpdir/etc/init.d/$base" + } ($base, @{$aliases{$name}}); + if (@sysv > 0 || $dh{ASSUME_SYSV_PRESENT}) { + $unitfiles{$name} = 'sysv'; + } else { + $unitfiles{$name} = 'systemd-only'; + } + } + + # Calls autoscript() as appropriate. + # Called once for all systemd files that have a corresponding SysV init + # script (invoke-rc.d handles start/stop/restart) and once for all + # systemd files without a corresponding SysV init script (systemctl + # handles start/stop/restart). + my $add_scripts = sub { + my ($units, $sysv_present) = @_; + + return 0 if @$units == 0; + + # The $package and $sed parameters are always the same. + # This wrapper function makes the following logic easier to read. + my $sd_autoscript = sub { + my ($script, $filename) = @_; + my $unitargs = join(" ", map { basename($_) } @$units); + autoscript($package, $script, $filename, "s/#UNITFILES#/$unitargs/"); + }; + + if (! $dh{NO_ENABLE}) { + if ($sysv_present) { + $sd_autoscript->("postinst", "postinst-systemd-enable"); + } elsif ($dh{RESTART_AFTER_UPGRADE}) { + $sd_autoscript->("postinst", "postinst-systemd-enable-restart"); + } elsif ($dh{NO_START}) { + # RESTART_AFTER_UPGRADE takes precedence + $sd_autoscript->("postinst", "postinst-systemd-enable"); + } else { + $sd_autoscript->("postinst", "postinst-systemd-enable-start"); + } + } else { + if (!$sysv_present && $dh{RESTART_AFTER_UPGRADE}) { + $sd_autoscript->("postinst", "postinst-systemd-restart"); + } elsif (!$sysv_present) { + # We need to stop/start before/after the upgrade. + $sd_autoscript->("postinst", "postinst-systemd-start"); + } + } + + if (! $dh{NO_ENABLE}) { + # These autoscripts contain a call to deb-systemd-helper disable, + # which needs to have all Aliases passed explicitly + # in order to properly cleanup the state file (the + # information is stored only in the symlinks which the + # admin might have removed). + my $filename = 'postrm-systemd'; + $filename .= '-reload' if !$sysv_present; + + my @both = @$units; + for my $unit (@$units) { + @both = (@both, @{$aliases{$unit}}); + } + + my $unitargs = join(" ", map { basename($_) } @both); + autoscript($package, "postrm", $filename, "s/#UNITFILES#/$unitargs/"); + } else { + if (!$sysv_present) { + $sd_autoscript->("postrm", "postrm-systemd-reload-only"); + } + } + + if (!$sysv_present) { + if ($dh{R_FLAG} || $dh{RESTART_AFTER_UPGRADE}) { + # stop service only on remove + $sd_autoscript->("prerm", "prerm-systemd-restart"); + } elsif (!$dh{NO_START}) { + # always stop service + $sd_autoscript->("prerm", "prerm-systemd"); + } + } + + return 1; + }; + + if (($add_scripts->([ grep { $unitfiles{$_} eq 'systemd-only' } keys %unitfiles ], 0) + + $add_scripts->([ grep { $unitfiles{$_} eq 'sysv' } keys %unitfiles ], 1)) > 0) { + # init-system-helpers ships deb-systemd-helper which we use in + # our autoscripts + addsubstvar($package, "misc:Depends", "init-system-helpers"); + } +} + +=head1 SEE ALSO + +L + +=head1 AUTHORS + +pkg-systemd-maintainers@lists.alioth.debian.org + +=cut -- cgit v1.2.3