Control: severity -1 important
Control: tags -1 + patch

Dear Maintainer,

(Cc systemd maintainers, because it's of interest to them.)

I have created a patch that implements the previously missing parts of
systemd integration into update-rc.d, following along the lines of the
patch I have written for deb-systemd-helper (bug #780522).

The patch now makes update-rc.d support:

 - drop-in configuration snippets
 - multiple words per setting (i.e. "WantedBy=a b")
 - RequiredBy=, Alias=, Also=

(Note that the patch currently implements the full systemd logic of
also looking at /run/systemd/generator*, which may or may not be
something one wants or not - I would defer to the systemd team as to
whether they'd want to see that included - but it should be consistent
across update-rc.d and deb-systemd-helper.)

Since systemctl enable/disable calls out to update-rc.d, I think this
is something that should definitely be fixed for Jessie (otherwise,
doing systemctl (en|dis)able something-with-alias.service will not work
properly!).

I have diverted update-rc.d on my local machine and replaced it with
the patched version. I've also tested it with some examples and as far
as I can tell it works properly.

Christian
diff -Nru sysvinit-2.88dsf/debian/src/sysv-rc/sbin/update-rc.d 
sysvinit-2.88dsf/debian/src/sysv-rc/sbin/update-rc.d
--- sysvinit-2.88dsf/debian/src/sysv-rc/sbin/update-rc.d        2014-10-25 
23:15:12.000000000 +0200
+++ sysvinit-2.88dsf/debian/src/sysv-rc/sbin/update-rc.d        2015-03-15 
15:49:27.000000000 +0100
@@ -7,6 +7,7 @@
 use warnings;
 # NB: All Perl modules used here must be in perl-base. Specifically, depending
 # on modules in perl-modules is not okay! See bug #716923
+use Text::ParseWords qw(shellwords); # in core since Perl 5
 
 my $initd = "/etc/init.d";
 my $etcd  = "/etc/rc";
@@ -63,46 +64,121 @@
     map { push @dirs, $_; mkdir join('/', @dirs), 0755; } @path;
 }
 
+# Recursively deletes a directory structure, if all (!) components are empty,
+# e.g. to clean up after purging.
+# (Taken from deb-systemd-helper)
+sub rmdir_if_empty {
+    my ($dir) = @_;
+
+    rmdir_if_empty($_) for (grep { -d } <$dir/*>);
+    rmdir($dir);
+}
+
 # Creates the necessary links to enable/disable the service (equivalent of an
 # initscript) in systemd.
 sub make_systemd_links {
-    my ($scriptname, $action) = @_;
+    my ($scriptname, $action, $recursive) = @_;
+
+    my @unit_paths = (
+        "/run/systemd/generator.late",
+        "/lib/systemd/system",
+        "/run/systemd/generator",
+        "/etc/systemd/system",
+        "/run/systemd/generator.early"
+    );
 
     # In addition to the insserv call we also enable/disable the service
     # for systemd by creating the appropriate symlink in case there is a
     # native systemd service. We need to do this on our own instead of
     # using systemctl because systemd might not even be installed yet.
-    my $service_path;
-    if (-f "/etc/systemd/system/$scriptname.service") {
-        $service_path = "/etc/systemd/system/$scriptname.service";
-    } elsif (-f "/lib/systemd/system/$scriptname.service") {
-        $service_path = "/lib/systemd/system/$scriptname.service";
-    }
-    if (defined($service_path)) {
-        my $changed_sth;
-        open my $fh, '<', $service_path or error("unable to read 
$service_path");
-        while (<$fh>) {
-            chomp;
-            if (/^\s*WantedBy=(.+)$/i) {
-                my $wants_dir = "/etc/systemd/system/$1.wants";
-                my $service_link = "$wants_dir/$scriptname.service";
-                if ("enable" eq $action) {
-                    make_path($wants_dir);
-                    symlink($service_path, $service_link);
+
+    # The following code is adapted from deb-systemd-helper
+    my ($service_path, %dropins);
+
+    foreach (@unit_paths) {
+        if (-f "$_/$scriptname") {
+            $service_path = "$_/$scriptname";
+        }
+        foreach (<$_/$scriptname.d/*.conf>) {
+            $dropins{basename($_)} = $_;
+        }
+    }
+
+    return unless defined($service_path);
+
+    my $changed_sth = 0;
+    my $unit_config = {
+        'WantedBy' => [],
+        'RequiredBy' => [],
+        'Also' => [],
+        'Alias' => []
+    };
+
+    foreach ($service_path, sort values %dropins) {
+        open my $fh, '<', $_ or error("unable to read $_");
+        while (my $line = <$fh>) {
+            chomp($line);
+            if ($line =~ /^\s*(WantedBy|RequiredBy|Also|Alias)=(.*)$/i) {
+                my @values = shellwords($2);
+
+                # systemd will reset a list to empty if it encounters an
+                # empty value
+                if (scalar (@values) == 0) {
+                    $unit_config->{$1} = [];
                 } else {
-                    unlink($service_link) if -e $service_link;
+                    push(@{$unit_config->{$1}}, @values);
                 }
-                $changed_sth = 1;
             }
         }
         close($fh);
+    }
+
+    for my $value (@{$unit_config->{'WantedBy'}}) {
+        my $wants_dir = "/etc/systemd/system/$value.wants";
+        my $service_link = "$wants_dir/$scriptname";
+        if ("enable" eq $action) {
+            make_path($wants_dir);
+            symlink($service_path, $service_link);
+        } else {
+            unlink($service_link) if -e $service_link;
+            rmdir_if_empty($wants_dir);
+        }
+        $changed_sth = 1;
+    }
+
+    for my $value (@{$unit_config->{'RequiredBy'}}) {
+        my $requires_dir = "/etc/systemd/system/$value.requires";
+        my $service_link = "$requires_dir/$scriptname";
+        if ("enable" eq $action) {
+            make_path($requires_dir);
+            symlink($service_path, $service_link);
+        } else {
+            unlink($service_link) if -e $service_link;
+            rmdir_if_empty($requires_dir);
+        }
+        $changed_sth = 1;
+    }
 
-        # If we changed anything and this machine is running systemd, tell
-        # systemd to reload so that it will immediately pick up our
-        # changes.
-        if ($changed_sth && -d "/run/systemd/system") {
-            system("systemctl", "daemon-reload");
+    for my $value (@{$unit_config->{'Alias'}}) {
+        my $link = "/etc/systemd/system/$value";
+        if ("enable" eq $action) {
+            symlink($service_path, $link);
+        } else {
+            unlink($link) if -e $link;
         }
+        $changed_sth = 1;
+    }
+
+    for my $value (@{$unit_config->{'Also'}}) {
+        make_systemd_links($value, $action, 1);
+        $changed_sth = 1;
+    }
+
+    # If we changed anything and this machine is running systemd, tell
+    # systemd to reload so that it will immediately pick up our
+    # changes. Only do this if we aren't calling ourselves recursively.
+    if (!$recursive && $changed_sth && -d "/run/systemd/system") {
+        system("systemctl", "daemon-reload");
     }
 }
 
@@ -213,7 +289,9 @@
             error("initscript does not exist: /etc/init.d/$scriptname");
         }
     } elsif ("disable" eq $action || "enable" eq $action) {
-        make_systemd_links($scriptname, $action);
+        # add .service suffix here because make_systemd_links must
+        # be able to call itself recursively for Also= in units
+        make_systemd_links("$scriptname.service", $action, 0);
 
         upstart_toggle($scriptname, $action);
 
_______________________________________________
Pkg-systemd-maintainers mailing list
Pkg-systemd-maintainers@lists.alioth.debian.org
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-systemd-maintainers

Reply via email to