Package: debhelper Version: 9.20130518 Severity: wishlist Tags: patch X-Debbugs-CC: pkg-systemd-maintain...@lists.alioth.debian.org
Hi Joey, I have been working on dh_installsystemd lately and I am confident that it is ready for inclusion into debhelper after being tested for several weeks by several maintainers. Find the patch attached, and here is a quote from its commit message: ---------------------------------------------------------------------- This helper is called automatically by dh(1). It enables systemd unit files and handles start/stop/restart on upgrade for unit files without a corresponding SysV init script. In order to enable systemd unit files, a dependency on init-system-helpers is added to the package. This dependency is necessary to ensure deb-systemd-helper is present, which will enable unit files on any machine, regardless of whether it runs systemd or not. This in turn is a requirement for switching from sysvinit to systemd and vice-versa. The raison d'être for dh_installsystemd is the fact that some packages ship multiple service files but only a single SysV init script, i.e. there is no 1:1 correlation between systemd unit files and SysV init scripts. As an example, samba consists of smbd and nmbd, but both are started using /etc/init.d/samba, whereas with systemd, one might use smbd.service and nmbd.service instead. For such cases it makes sense to have a separate helper instead of trying to cram the functionality into dh_installinit. dh_installsystemd was tested with numerous packages that use different sets of features. Among the tested packages are alsa-utils, rsyslog, dbus, nginx, udisks, irker, pyroman, all-knowing-dns and kanla. In case dh_installsystemd adds undesirable code to your maintscripts, please contact pkg-systemd-maintain...@lists.alioth.debian.org and temporarily disable dh_installsystemd with this make rule: # Disable dh_installsystemd override_dh_installsystemd: ---------------------------------------------------------------------- To be clear about this: dh_installsystemd does _not_ replace dh_installinit or any of its code, not even the systemd-related code. In case you have any questions, please feel free to ask :). Thanks! -- Best regards, Michael
>From 915a73b1423736b1fdb0c827ef7a0b87d7e6d667 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg <mich...@stapelberg.de> Date: Sat, 4 May 2013 20:46:01 +0200 Subject: [PATCH] add new helper dh_installsystemd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helper is called automatically by dh(1). It enables systemd unit files and handles start/stop/restart on upgrade for unit files without a corresponding SysV init script. In order to enable systemd unit files, a dependency on init-system-helpers is added to the package. This dependency is necessary to ensure deb-systemd-helper is present, which will enable unit files on any machine, regardless of whether it runs systemd or not. This in turn is a requirement for switching from sysvinit to systemd and vice-versa. The raison d'être for dh_installsystemd is the fact that some packages ship multiple service files but only a single SysV init script, i.e. there is no 1:1 correlation between systemd unit files and SysV init scripts. As an example, samba consists of smbd and nmbd, but both are started using /etc/init.d/samba, whereas with systemd, one might use smbd.service and nmbd.service instead. For such cases it makes sense to have a separate helper instead of trying to cram the functionality into dh_installinit. dh_installsystemd was tested with numerous packages that use different sets of features. Among the tested packages are alsa-utils, rsyslog, dbus, nginx, udisks, irker, pyroman, all-knowing-dns and kanla. In case dh_installsystemd adds undesirable code to your maintscripts, please contact pkg-systemd-maintain...@lists.alioth.debian.org and temporarily disable dh_installsystemd with this make rule: # Disable dh_installsystemd override_dh_installsystemd: --- autoscripts/postinst-systemd-enable | 1 + autoscripts/postinst-systemd-enable-restart | 6 + autoscripts/postinst-systemd-enable-start | 6 + autoscripts/postinst-systemd-restart | 3 + autoscripts/postinst-systemd-start | 3 + autoscripts/postrm-systemd | 5 + autoscripts/postrm-systemd-reload | 9 + autoscripts/postrm-systemd-reload-only | 3 + autoscripts/prerm-systemd | 3 + autoscripts/prerm-systemd-restart | 3 + dh | 1 + dh_installsystemd | 312 +++++++++++++++++++++++++++ 12 files changed, 355 insertions(+) create mode 100644 autoscripts/postinst-systemd-enable create mode 100644 autoscripts/postinst-systemd-enable-restart create mode 100644 autoscripts/postinst-systemd-enable-start create mode 100644 autoscripts/postinst-systemd-restart create mode 100644 autoscripts/postinst-systemd-start create mode 100644 autoscripts/postrm-systemd create mode 100644 autoscripts/postrm-systemd-reload create mode 100644 autoscripts/postrm-systemd-reload-only create mode 100644 autoscripts/prerm-systemd create mode 100644 autoscripts/prerm-systemd-restart create mode 100755 dh_installsystemd diff --git a/autoscripts/postinst-systemd-enable b/autoscripts/postinst-systemd-enable new file mode 100644 index 0000000..052dc0e --- /dev/null +++ b/autoscripts/postinst-systemd-enable @@ -0,0 +1 @@ +deb-systemd-helper enable #UNITFILES# >/dev/null || true diff --git a/autoscripts/postinst-systemd-enable-restart b/autoscripts/postinst-systemd-enable-restart new file mode 100644 index 0000000..a63aa6b --- /dev/null +++ b/autoscripts/postinst-systemd-enable-restart @@ -0,0 +1,6 @@ +deb-systemd-helper enable #UNITFILES# >/dev/null || true + +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + systemctl try-restart #UNITFILES# >/dev/null || true +fi diff --git a/autoscripts/postinst-systemd-enable-start b/autoscripts/postinst-systemd-enable-start new file mode 100644 index 0000000..6cccdcf --- /dev/null +++ b/autoscripts/postinst-systemd-enable-start @@ -0,0 +1,6 @@ +deb-systemd-helper enable #UNITFILES# >/dev/null || true + +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true + systemctl start #UNITFILES# >/dev/null || true +fi diff --git a/autoscripts/postinst-systemd-restart b/autoscripts/postinst-systemd-restart new file mode 100644 index 0000000..e96bc93 --- /dev/null +++ b/autoscripts/postinst-systemd-restart @@ -0,0 +1,3 @@ +if [ -d /run/systemd/system ]; then + systemctl try-restart #UNITFILES# >/dev/null || true +fi diff --git a/autoscripts/postinst-systemd-start b/autoscripts/postinst-systemd-start new file mode 100644 index 0000000..bf6458b --- /dev/null +++ b/autoscripts/postinst-systemd-start @@ -0,0 +1,3 @@ +if [ -d /run/systemd/system ]; then + systemctl start #UNITFILES# >/dev/null || true +fi diff --git a/autoscripts/postrm-systemd b/autoscripts/postrm-systemd new file mode 100644 index 0000000..ddc2269 --- /dev/null +++ b/autoscripts/postrm-systemd @@ -0,0 +1,5 @@ +if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper disable #UNITFILES# >/dev/null + fi +fi diff --git a/autoscripts/postrm-systemd-reload b/autoscripts/postrm-systemd-reload new file mode 100644 index 0000000..d86be20 --- /dev/null +++ b/autoscripts/postrm-systemd-reload @@ -0,0 +1,9 @@ +if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then + if [ -x "/usr/bin/deb-systemd-helper" ]; then + deb-systemd-helper disable #UNITFILES# >/dev/null + fi +fi + +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true +fi diff --git a/autoscripts/postrm-systemd-reload-only b/autoscripts/postrm-systemd-reload-only new file mode 100644 index 0000000..91cd9e8 --- /dev/null +++ b/autoscripts/postrm-systemd-reload-only @@ -0,0 +1,3 @@ +if [ -d /run/systemd/system ]; then + systemctl --system daemon-reload >/dev/null || true +fi diff --git a/autoscripts/prerm-systemd b/autoscripts/prerm-systemd new file mode 100644 index 0000000..aa36110 --- /dev/null +++ b/autoscripts/prerm-systemd @@ -0,0 +1,3 @@ +if [ -d /run/systemd/system ]; then + systemctl stop #UNITFILES# >/dev/null +fi diff --git a/autoscripts/prerm-systemd-restart b/autoscripts/prerm-systemd-restart new file mode 100644 index 0000000..6f4e584 --- /dev/null +++ b/autoscripts/prerm-systemd-restart @@ -0,0 +1,3 @@ +if [ -d /run/systemd/system ] && [ "$1" = remove ]; then + systemctl stop #UNITFILES# >/dev/null +fi diff --git a/dh b/dh index 7f81661..d8db5ac 100755 --- a/dh +++ b/dh @@ -373,6 +373,7 @@ my @i = qw{ dh_installifupdown dh_installinfo dh_installinit + dh_installsystemd dh_installmenu dh_installmime dh_installmodules diff --git a/dh_installsystemd b/dh_installsystemd new file mode 100755 index 0000000..a97b5e8 --- /dev/null +++ b/dh_installsystemd @@ -0,0 +1,312 @@ +#!/usr/bin/perl -w + +=head1 NAME + +dh_installsystemd - 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<dh_installsystemd> [S<I<debhelper options>>] [B<--no-enable>] [B<--restart-after-upgrade>] [B<--no-restart-on-upgrade>] [B<--assume-sysv-present>] [S<I<unit file> ...>] + +=head1 DESCRIPTION + +B<dh_installsystemd> 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<dh_installinit>, 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<dh_installsystemd> 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_installsystemd --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<dh_installsystemd> for a specific unit file. + +Example (see DESCRIPTION): + dh_installsystemd --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<prerm> and starts it again in the F<postinst> 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<dh_installsystemd> before B<dh_installinit>, init scripts might +not be installed yet and thus cannot be found by B<dh_installsystemd>. 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. + +=back + +=head1 EXAMPLES + +Note that B<dh> calls B<dh_installsystemd> by default, so in most cases you do +not need to change anything at all. + +Here is an example make target to use in debian/rules: + + override_dh_installsystemd: + # The user will enable smartd.service if she wants to run it. + # Call dh_installsystemd to stop/start the service on upgrades. + dh_installsystemd --no-enable smartd.service + +=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. + +Note that B<dh_installsystemd> should be run after B<dh_installinit> so that it +can detect corresponding SysV init scripts. The default sequence in B<dh> does +the right thing, this note is only relevant when you are calling +B<dh_installsystemd> 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. + # The fact that we also try start the service + # once after installing is just a minor + # inconvenience (for now). + $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<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHORS + +pkg-systemd-maintain...@lists.alioth.debian.org + +=cut -- 1.7.10.4