Bug#890509: Implemented user units support in deb-systemd-helper

Daniele Nicolodi daniele at grinta.net
Sun Jun 3 03:53:14 BST 2018


Control: tags -1 patch

Hello,

I implemented user units support in deb-systemd-helper.

Its tests were not working properly on my system, so I took the occasion
to revise a bit the test infrastructure and drop the dependency to the
unpackaged Linux::Clone Perl module (at least in the test mode used by
autopkgtests).

Introducing the new functionality was relatively easy, most of the
patches deal with improving the tests infrastructure.

Patches are attached. Please review them and let me know.

Cheers,
Dan
-------------- next part --------------
From 97c8238716808ec714317e57533ad58f6cab1ddd Mon Sep 17 00:00:00 2001
From: Daniele Nicolodi <daniele at grinta.net>
Date: Sat, 2 Jun 2018 20:28:17 -0600
Subject: [PATCH 10/10] tests: Make calling 'deb-systemd-helper' in tests less
 verbose

Removing the shell interposition in the system() calls avoids edge
cases in parameters handling (there were places where the randomly
generated unit names were not correctly quoted) and speeds up tests
execution.
---
 t/001-deb-systemd-helper.t         | 68 +++++++++++++++--------------
 t/002-deb-systemd-helper-update.t  | 14 +++---
 t/003-deb-systemd-helper-complex.t |  4 +-
 t/004-deb-systemd-helper-user.t    | 70 +++++++++++++++---------------
 t/helpers.pm                       |  8 +++-
 5 files changed, 84 insertions(+), 80 deletions(-)

diff --git a/t/001-deb-systemd-helper.t b/t/001-deb-systemd-helper.t
index a2c96c3..c603dba 100644
--- a/t/001-deb-systemd-helper.t
+++ b/t/001-deb-systemd-helper.t
@@ -14,8 +14,6 @@ use helpers;
 
 test_setup();
 
-my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
-
 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 # ┃ Verify “is-enabled” is not true for a random, non-existing unit file.     ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
@@ -62,7 +60,7 @@ unless ($ENV{'TEST_ON_REAL_SYSTEM'}) {
        'multi-user.target.wants does not exist yet');
 }
 
-my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable '$random_unit'");
+my $retval = dsh('enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 my $symlink_path = "/etc/systemd/system/multi-user.target.wants/$random_unit";
 ok(-l $symlink_path, "$random_unit was enabled");
@@ -86,7 +84,7 @@ ok(! -l $symlink_path, 'symlink deleted');
 isnt_enabled($random_unit);
 is_debian_installed($random_unit);
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable '$random_unit'");
+$retval = dsh('enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 
 isnt_enabled($random_unit);
@@ -99,7 +97,9 @@ my $statefile = "/var/lib/systemd/deb-systemd-helper-enabled/$random_unit.dsh-al
 
 ok(-f $statefile, 'state file exists');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test _DEB_SYSTEMD_HELPER_PURGE=1 $dsh disable '$random_unit'");
+$ENV{'_DEB_SYSTEMD_HELPER_PURGE'} = '1';
+$retval = dsh('disable', $random_unit);
+delete $ENV{'_DEB_SYSTEMD_HELPER_PURGE'};
 is($retval, 0, "disable command succeeded");
 ok(! -f $statefile, 'state file does not exist anymore after purging');
 isnt_debian_installed($random_unit);
@@ -111,7 +111,7 @@ isnt_debian_installed($random_unit);
 ok(! -l $symlink_path, 'symlink does not exist yet');
 isnt_enabled($random_unit);
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable '$random_unit'");
+$retval = dsh('enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 
 is_enabled($random_unit);
@@ -121,7 +121,9 @@ is_debian_installed($random_unit);
 # ┃ Verify “disable” removes the symlinks.                                    ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test _DEB_SYSTEMD_HELPER_PURGE=1 $dsh disable '$random_unit'");
+$ENV{'_DEB_SYSTEMD_HELPER_PURGE'} = '1';
+$retval = dsh('disable', $random_unit);
+delete $ENV{'_DEB_SYSTEMD_HELPER_PURGE'};
 is($retval, 0, "disable command succeeded");
 
 isnt_enabled($random_unit);
@@ -133,7 +135,7 @@ isnt_enabled($random_unit);
 ok(! -l $symlink_path, 'symlink does not exist yet');
 isnt_enabled($random_unit);
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable '$random_unit'");
+$retval = dsh('enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 
 is_enabled($random_unit);
@@ -143,7 +145,7 @@ is_debian_installed($random_unit);
 # ┃ Verify the “purge” verb works.                                            ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh purge '$random_unit'");
+$retval = dsh('purge', $random_unit);
 is($retval, 0, "purge command succeeded");
 
 isnt_enabled($random_unit);
@@ -156,7 +158,7 @@ isnt_debian_installed($random_unit);
 ok(! -l $symlink_path, 'symlink does not exist yet');
 isnt_enabled($random_unit);
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable '$random_unit'");
+$retval = dsh('enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 
 is_enabled($random_unit);
@@ -169,12 +171,12 @@ is_debian_installed($random_unit);
 my $mask_path = "/etc/systemd/system/$random_unit";
 ok(! -l $mask_path, 'mask link does not exist yet');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh mask '$random_unit'");
+$retval = dsh('mask', $random_unit);
 is($retval, 0, "mask command succeeded");
 ok(-l $mask_path, 'mask link exists');
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh unmask '$random_unit'");
+$retval = dsh('unmask', $random_unit);
 is($retval, 0, "unmask command succeeded");
 ok(! -e $mask_path, 'mask link does not exist anymore');
 
@@ -182,16 +184,16 @@ ok(! -e $mask_path, 'mask link does not exist anymore');
 # ┃ Verify “mask” (when disabled) works the same way                          ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh disable '$random_unit'");
+$retval = dsh('disable', $random_unit);
 is($retval, 0, "disable command succeeded");
 ok(! -e $symlink_path, 'symlink no longer exists');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh mask '$random_unit'");
+$retval = dsh('mask', $random_unit);
 is($retval, 0, "mask command succeeded");
 ok(-l $mask_path, 'mask link exists');
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh unmask '$random_unit'");
+$retval = dsh('unmask', $random_unit);
 is($retval, 0, "unmask command succeeded");
 ok(! -e $mask_path, 'symlink no longer exists');
 
@@ -204,12 +206,12 @@ symlink('/dev/null', $mask_path);
 ok(-l $mask_path, 'mask link exists');
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh mask '$random_unit'");
+$retval = dsh('mask', $random_unit);
 is($retval, 0, "mask command succeeded");
 ok(-l $mask_path, 'mask link exists');
 is(readlink($mask_path), '/dev/null', 'service still masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh unmask '$random_unit'");
+$retval = dsh('unmask', $random_unit);
 is($retval, 0, "unmask command succeeded");
 ok(-l $mask_path, 'mask link exists');
 is(readlink($mask_path), '/dev/null', 'service still masked');
@@ -236,14 +238,14 @@ close($fh);
 ok(-e $mask_path, 'local service file exists');
 ok(! -l $mask_path, 'local service file is not a symlink');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh mask '$random_unit'");
+$retval = dsh('mask', $random_unit);
 isnt($retval, -1, 'deb-systemd-helper could be executed');
 ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
 is($retval >> 8, 0, 'deb-systemd-helper exited with exit code 0');
 ok(-e $mask_path, 'local service file still exists');
 ok(! -l $mask_path, 'local service file is still not a symlink');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh unmask '$random_unit'");
+$retval = dsh('unmask', $random_unit);
 isnt($retval, -1, 'deb-systemd-helper could be executed');
 ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
 is($retval >> 8, 0, 'deb-systemd-helper exited with exit code 0');
@@ -256,7 +258,7 @@ unlink($mask_path);
 # ┃ Verify Alias= handling.                                                   ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh purge '$random_unit'");
+$retval = dsh('purge', $random_unit);
 is($retval, 0, "purge command succeeded");
 
 open($fh, '>', $servicefile_path);
@@ -277,23 +279,23 @@ isnt_enabled($random_unit);
 isnt_enabled('foo\x2dtest.service');
 my $alias_path = '/etc/systemd/system/foo\x2dtest.service';
 ok(! -l $alias_path, 'alias link does not exist yet');
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable '$random_unit'");
+$retval = dsh('enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 is(readlink($alias_path), $servicefile_path, 'correct alias link');
 is_enabled($random_unit);
 ok(! -l $mask_path, 'mask link does not exist yet');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh mask '$random_unit'");
+$retval = dsh('mask', $random_unit);
 is($retval, 0, "mask command succeeded");
 is(readlink($alias_path), $servicefile_path, 'correct alias link');
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh unmask '$random_unit'");
+$retval = dsh('unmask', $random_unit);
 is($retval, 0, "unmask command succeeded");
 is(readlink($alias_path), $servicefile_path, 'correct alias link');
 ok(! -l $mask_path, 'mask link does not exist any more');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh disable '$random_unit'");
+$retval = dsh('disable', $random_unit);
 isnt_enabled($random_unit);
 ok(! -l $alias_path, 'alias link does not exist any more');
 
@@ -301,25 +303,25 @@ ok(! -l $alias_path, 'alias link does not exist any more');
 # ┃ Verify Alias/mask with removed package (as in postrm)                     ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh purge '$random_unit'");
+$retval = dsh('purge', $random_unit);
 is($retval, 0, "purge command succeeded");
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable '$random_unit'");
+$retval = dsh('enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 is(readlink($alias_path), $servicefile_path, 'correct alias link');
 
 unlink($servicefile_path);
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh mask '$random_unit'");
+$retval = dsh('mask', $random_unit);
 is($retval, 0, "mask command succeeded with uninstalled unit");
 is(readlink($alias_path), $servicefile_path, 'correct alias link');
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh purge '$random_unit'");
+$retval = dsh('purge', $random_unit);
 is($retval, 0, "purge command succeeded with uninstalled unit");
 ok(! -l $alias_path, 'alias link does not exist any more');
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh unmask '$random_unit'");
+$retval = dsh('unmask', $random_unit);
 is($retval, 0, "unmask command succeeded with uninstalled unit");
 ok(! -l $mask_path, 'mask link does not exist any more');
 
@@ -344,7 +346,7 @@ close($fh);
 isnt_enabled($random_unit);
 isnt_enabled('foo\x2dtest.service');
 # note that in this case $alias_path and $mask_path are identical
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable '$random_unit'");
+$retval = dsh('enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 is_enabled($random_unit);
 # systemctl enable does create the alias link even if it's not needed
@@ -352,15 +354,15 @@ is_enabled($random_unit);
 
 unlink($servicefile_path);
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh mask '$random_unit'");
+$retval = dsh('mask', $random_unit);
 is($retval, 0, "mask command succeeded");
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh unmask '$random_unit'");
+$retval = dsh('unmask', $random_unit);
 is($retval, 0, "unmask command succeeded");
 ok(! -l $mask_path, 'mask link does not exist any more');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh purge '$random_unit'");
+$retval = dsh('purge', $random_unit);
 isnt_enabled($random_unit);
 ok(! -l $mask_path, 'mask link does not exist any more');
 
diff --git a/t/002-deb-systemd-helper-update.t b/t/002-deb-systemd-helper-update.t
index 647002f..85913e9 100644
--- a/t/002-deb-systemd-helper-update.t
+++ b/t/002-deb-systemd-helper-update.t
@@ -14,8 +14,6 @@ use helpers;
 
 test_setup();
 
-my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
-
 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 # ┃ Verify “is-enabled” is not true for a random, non-existing unit file.     ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
@@ -47,7 +45,7 @@ close($fh);
 # ┃ Verify “enable” creates the requested symlinks.                           ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
 
-my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable $random_unit");
+my $retval = dsh('enable', $random_unit);
 my $symlink_path = "/etc/systemd/system/multi-user.target.wants/$random_unit";
 ok(-l $symlink_path, "$random_unit was enabled");
 is(readlink($symlink_path), $servicefile_path,
@@ -73,7 +71,7 @@ isnt_enabled($random_unit);
 # ┃ Verify “was-enabled” is still true (operates on the state file).          ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh was-enabled $random_unit");
+$retval = dsh('was-enabled', $random_unit);
 isnt($retval, -1, 'deb-systemd-helper could be executed');
 ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
 is($retval >> 8, 0, "random unit file was-enabled");
@@ -94,7 +92,7 @@ is_deeply(
 my $new_symlink_path = '/etc/systemd/system/newalias.service';
 ok(! -l $new_symlink_path, 'new symlink does not exist yet');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable $random_unit");
+$retval = dsh('enable', $random_unit);
 ok(-l $new_symlink_path, 'new symlink was created');
 is(readlink($new_symlink_path), $servicefile_path,
     "symlink points to $servicefile_path");
@@ -124,7 +122,7 @@ isnt_enabled($random_unit);
 # ┃ Verify “was-enabled” is still true (operates on the state file).          ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh was-enabled $random_unit");
+$retval = dsh('was-enabled', $random_unit);
 isnt($retval, -1, 'deb-systemd-helper could be executed');
 ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
 is($retval >> 8, 0, "random unit file was-enabled");
@@ -146,7 +144,7 @@ is_deeply(
 my $new_symlink_path2 = '/etc/systemd/system/another.service';
 ok(! -l $new_symlink_path2, 'new symlink does not exist yet');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh update-state $random_unit");
+$retval = dsh('update-state', $random_unit);
 ok(! -l $new_symlink_path2, 'new symlink still does not exist');
 
 isnt_enabled($random_unit);
@@ -179,7 +177,7 @@ unlink($new_symlink_path);
 ok(! -l $new_symlink_path, 'new symlink still does not exist');
 ok(! -l $new_symlink_path2, 'new symlink 2 still does not exist');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh update-state $random_unit");
+$retval = dsh('update-state', $random_unit);
 
 ok(! -l $new_symlink_path, 'new symlink still does not exist');
 ok(! -l $new_symlink_path2, 'new symlink 2 still does not exist');
diff --git a/t/003-deb-systemd-helper-complex.t b/t/003-deb-systemd-helper-complex.t
index 589b9bf..c496995 100644
--- a/t/003-deb-systemd-helper-complex.t
+++ b/t/003-deb-systemd-helper-complex.t
@@ -14,8 +14,6 @@ use helpers;
 
 test_setup();
 
-my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
-
 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 # ┃ Create two unit files with random names; one refers to the other (Also=). ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
@@ -80,7 +78,7 @@ unless ($ENV{'TEST_ON_REAL_SYSTEM'}) {
        'multi-user.target.wants does not exist yet');
 }
 
-my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable $random_unit1");
+my $retval = dsh('enable', $random_unit1);
 my %links = map { (basename($_), readlink($_)) }
     ("/etc/systemd/system/multi-user.target.wants/$random_unit1",
      "/etc/systemd/system/multi-user.target.wants/$random_unit2");
diff --git a/t/004-deb-systemd-helper-user.t b/t/004-deb-systemd-helper-user.t
index 798ebe8..4bb94cb 100644
--- a/t/004-deb-systemd-helper-user.t
+++ b/t/004-deb-systemd-helper-user.t
@@ -14,8 +14,6 @@ use helpers;
 
 test_setup();
 
-my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
-
 # 
 # "is-enabled" is not true for a random, non-existing unit file
 #
@@ -66,7 +64,7 @@ unless ($ENV{'TEST_ON_REAL_SYSTEM'}) {
        'default.target.wants does not exist yet');
 }
 
-my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+my $retval = dsh('--user', 'enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 my $symlink_path = "/etc/systemd/user/default.target.wants/$random_unit";
 ok(-l $symlink_path, "$random_unit was enabled");
@@ -93,7 +91,7 @@ isnt_enabled($random_unit, user => 1);
 isnt_debian_installed($random_unit);
 is_debian_installed($random_unit, user => 1);
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+$retval = dsh('--user', 'enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 
 isnt_enabled($random_unit, user => 1);
@@ -106,7 +104,9 @@ my $statefile = "/var/lib/systemd/deb-systemd-user-helper-enabled/$random_unit.d
 
 ok(-f $statefile, 'state file exists');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test _DEB_SYSTEMD_HELPER_PURGE=1 $dsh --user disable '$random_unit'");
+$ENV{'_DEB_SYSTEMD_HELPER_PURGE'} = '1';
+$retval = dsh('--user', 'disable', $random_unit);
+delete $ENV{'_DEB_SYSTEMD_HELPER_PURGE'};
 is($retval, 0, "disable command succeeded");
 ok(! -f $statefile, 'state file does not exist anymore after purging');
 isnt_debian_installed($random_unit);
@@ -120,7 +120,7 @@ ok(! -l $symlink_path, 'symlink does not exist yet');
 isnt_enabled($random_unit);
 isnt_enabled($random_unit, user => 1);
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+$retval = dsh('--user', 'enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 
 isnt_enabled($random_unit);
@@ -132,7 +132,9 @@ is_debian_installed($random_unit, user => 1);
 # "disable" removes the symlinks
 #
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test _DEB_SYSTEMD_HELPER_PURGE=1 $dsh --user disable '$random_unit'");
+$ENV{'_DEB_SYSTEMD_HELPER_PURGE'} = '1';
+$retval = dsh('--user', 'disable', $random_unit);
+delete $ENV{'_DEB_SYSTEMD_HELPER_PURGE'};
 is($retval, 0, "disable command succeeded");
 
 isnt_enabled($random_unit);
@@ -146,7 +148,7 @@ ok(! -l $symlink_path, 'symlink does not exist yet');
 isnt_enabled($random_unit);
 isnt_enabled($random_unit, user => 1);
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+$retval = dsh('--user', 'enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 
 isnt_enabled($random_unit);
@@ -158,7 +160,7 @@ is_debian_installed($random_unit, user => 1);
 # "purge" works
 #
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user purge '$random_unit'");
+$retval = dsh('--user', 'purge', $random_unit);
 is($retval, 0, "purge command succeeded");
 
 isnt_enabled($random_unit, user => 1);
@@ -171,7 +173,7 @@ isnt_debian_installed($random_unit, user => 1);
 ok(! -l $symlink_path, 'symlink does not exist yet');
 isnt_enabled($random_unit, user => 1);
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+$retval = dsh('--user', 'enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 
 is_enabled($random_unit, user => 1);
@@ -184,12 +186,12 @@ is_debian_installed($random_unit, user => 1);
 my $mask_path = "/etc/systemd/user/$random_unit";
 ok(! -l $mask_path, 'mask link does not exist yet');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+$retval = dsh('--user', 'mask', $random_unit);
 is($retval, 0, "mask command succeeded");
 ok(-l $mask_path, 'mask link exists');
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+$retval = dsh('--user', 'unmask', $random_unit);
 is($retval, 0, "unmask command succeeded");
 ok(! -e $mask_path, 'mask link does not exist anymore');
 
@@ -197,16 +199,16 @@ ok(! -e $mask_path, 'mask link does not exist anymore');
 # "mask" (when disabled) works the same way
 #
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user disable '$random_unit'");
+$retval = dsh('--user', 'disable', $random_unit);
 is($retval, 0, "disable command succeeded");
 ok(! -e $symlink_path, 'symlink no longer exists');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+$retval = dsh('--user', 'mask', $random_unit);
 is($retval, 0, "mask command succeeded");
 ok(-l $mask_path, 'mask link exists');
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+$retval = dsh('--user', 'unmask', $random_unit);
 is($retval, 0, "unmask command succeeded");
 ok(! -e $mask_path, 'symlink no longer exists');
 
@@ -219,12 +221,12 @@ symlink('/dev/null', $mask_path);
 ok(-l $mask_path, 'mask link exists');
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+$retval = dsh('--user', 'mask', $random_unit);
 is($retval, 0, "mask command succeeded");
 ok(-l $mask_path, 'mask link exists');
 is(readlink($mask_path), '/dev/null', 'service still masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+$retval = dsh('--user', 'unmask', $random_unit);
 is($retval, 0, "unmask command succeeded");
 ok(-l $mask_path, 'mask link exists');
 is(readlink($mask_path), '/dev/null', 'service still masked');
@@ -251,14 +253,14 @@ close($fh);
 ok(-e $mask_path, 'local service file exists');
 ok(! -l $mask_path, 'local service file is not a symlink');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+$retval = dsh('--user', 'mask', $random_unit);
 isnt($retval, -1, 'deb-systemd-helper could be executed');
 ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
 is($retval >> 8, 0, 'deb-systemd-helper exited with exit code 0');
 ok(-e $mask_path, 'local service file still exists');
 ok(! -l $mask_path, 'local service file is still not a symlink');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+$retval = dsh('--user', 'unmask', $random_unit);
 isnt($retval, -1, 'deb-systemd-helper could be executed');
 ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
 is($retval >> 8, 0, 'deb-systemd-helper exited with exit code 0');
@@ -271,7 +273,7 @@ unlink($mask_path);
 # "Alias=" handling
 #
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user purge '$random_unit'");
+$retval = dsh('--user', 'purge', $random_unit);
 is($retval, 0, "purge command succeeded");
 
 open($fh, '>', $servicefile_path);
@@ -292,23 +294,23 @@ isnt_enabled($random_unit, user => 1);
 isnt_enabled('foo\x2dtest.service', user => 1);
 my $alias_path = '/etc/systemd/user/foo\x2dtest.service';
 ok(! -l $alias_path, 'alias link does not exist yet');
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+$retval = dsh('--user', 'enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 is(readlink($alias_path), $servicefile_path, 'correct alias link');
 is_enabled($random_unit, user => 1);
 ok(! -l $mask_path, 'mask link does not exist yet');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+$retval = dsh('--user', 'mask', $random_unit);
 is($retval, 0, "mask command succeeded");
 is(readlink($alias_path), $servicefile_path, 'correct alias link');
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+$retval = dsh('--user', 'unmask', $random_unit);
 is($retval, 0, "unmask command succeeded");
 is(readlink($alias_path), $servicefile_path, 'correct alias link');
 ok(! -l $mask_path, 'mask link does not exist any more');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user disable '$random_unit'");
+$retval = dsh('--user', 'disable', $random_unit);
 isnt_enabled($random_unit, user => 1);
 ok(! -l $alias_path, 'alias link does not exist any more');
 
@@ -316,25 +318,25 @@ ok(! -l $alias_path, 'alias link does not exist any more');
 # "Alias=" / "mask" with removed package (as in postrm)
 #
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user purge '$random_unit'");
+$retval = dsh('--user', 'purge', $random_unit);
 is($retval, 0, "purge command succeeded");
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+$retval = dsh('--user', 'enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 is(readlink($alias_path), $servicefile_path, 'correct alias link');
 
 unlink($servicefile_path);
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+$retval = dsh('--user', 'mask', $random_unit);
 is($retval, 0, "mask command succeeded with uninstalled unit");
 is(readlink($alias_path), $servicefile_path, 'correct alias link');
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user purge '$random_unit'");
+$retval = dsh('--user', 'purge', $random_unit);
 is($retval, 0, "purge command succeeded with uninstalled unit");
 ok(! -l $alias_path, 'alias link does not exist any more');
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+$retval = dsh('--user', 'unmask', $random_unit);
 is($retval, 0, "unmask command succeeded with uninstalled unit");
 ok(! -l $mask_path, 'mask link does not exist any more');
 
@@ -359,7 +361,7 @@ close($fh);
 isnt_enabled($random_unit, user => 1);
 isnt_enabled('foo\x2dtest.service', user => 1);
 # note that in this case $alias_path and $mask_path are identical
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+$retval = dsh('--user', 'enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 is_enabled($random_unit, user => 1);
 # systemctl enable does create the alias link even if it's not needed
@@ -367,15 +369,15 @@ is_enabled($random_unit, user => 1);
 
 unlink($servicefile_path);
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+$retval = dsh('--user', 'mask', $random_unit);
 is($retval, 0, "mask command succeeded");
 is(readlink($mask_path), '/dev/null', 'service masked');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+$retval = dsh('--user', 'unmask', $random_unit);
 is($retval, 0, "unmask command succeeded");
 ok(! -l $mask_path, 'mask link does not exist any more');
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user purge '$random_unit'");
+$retval = dsh('--user', 'purge', $random_unit);
 isnt_enabled($random_unit, user => 1);
 ok(! -l $mask_path, 'mask link does not exist any more');
 
@@ -400,7 +402,7 @@ isnt_enabled($random_unit, user => 1);
 isnt_enabled('baz\x2dtest.service', user => 1);
 $alias_path = '/etc/systemd/user/baz\x2dtest.service';
 ok(! -l $alias_path, 'alias link does not exist yet');
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+$retval = dsh('--user', 'enable', $random_unit);
 is($retval, 0, "enable command succeeded");
 is_enabled($random_unit, user => 1);
 ok(-l $alias_path, 'alias link does exist');
diff --git a/t/helpers.pm b/t/helpers.pm
index 18120b4..cae7404 100644
--- a/t/helpers.pm
+++ b/t/helpers.pm
@@ -52,12 +52,16 @@ sub state_file_entries {
 }
 
 my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
+$ENV{'DPKG_MAINTSCRIPT_PACKAGE'} = 'deb-systemd-helper-test';
+
+sub dsh {
+    return system($dsh, @_);
+}
 
 sub _unit_check {
     my ($cmd, $cb, $verb, $unit, %opts) = @_;
-    my $user = $opts{'user'} ? '--user' : '';
 
-    my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh $user $cmd '$unit'");
+    my $retval = dsh($opts{'user'} ? '--user' : '--system', $cmd, $unit);
 
     isnt($retval, -1, 'deb-systemd-helper could be executed');
     ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
-- 
2.17.0

-------------- next part --------------
From 98baf2fe8946e5fd7818d79ed94046f255ca74fb Mon Sep 17 00:00:00 2001
From: Daniele Nicolodi <daniele at grinta.net>
Date: Sat, 2 Jun 2018 20:03:52 -0600
Subject: [PATCH 09/10] tests: Add tests for user instance's service handling

---
 t/004-deb-systemd-helper-user.t | 409 ++++++++++++++++++++++++++++++++
 t/helpers.pm                    |  27 ++-
 2 files changed, 424 insertions(+), 12 deletions(-)
 create mode 100644 t/004-deb-systemd-helper-user.t

diff --git a/t/004-deb-systemd-helper-user.t b/t/004-deb-systemd-helper-user.t
new file mode 100644
index 0000000..798ebe8
--- /dev/null
+++ b/t/004-deb-systemd-helper-user.t
@@ -0,0 +1,409 @@
+#!perl
+# vim:ts=4:sw=4:et
+
+use strict;
+use warnings;
+use Test::More;
+use File::Temp qw(tempfile tempdir); # in core since perl 5.6.1
+use File::Path qw(make_path); # in core since Perl 5.001
+use File::Basename; # in core since Perl 5
+use FindBin; # in core since Perl 5.00307
+
+use lib "$FindBin::Bin/.";
+use helpers;
+
+test_setup();
+
+my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
+
+# 
+# "is-enabled" is not true for a random, non-existing unit file
+#
+
+my ($fh, $random_unit) = tempfile('unit\x2dXXXXX',
+    SUFFIX => '.service',
+    TMPDIR => 1,
+    UNLINK => 1);
+close($fh);
+$random_unit = basename($random_unit);
+
+isnt_enabled($random_unit);
+isnt_enabled($random_unit, user => 1);
+isnt_debian_installed($random_unit);
+isnt_debian_installed($random_unit, user => 1);
+
+#
+# "is-enabled" is not true for a random, existing user unit file
+#
+
+my $servicefile_path = "/usr/lib/systemd/user/$random_unit";
+make_path('/usr/lib/systemd/user');
+open($fh, '>', $servicefile_path);
+print $fh <<'EOT';
+[Unit]
+Description=test unit
+
+[Service]
+ExecStart=/bin/sleep 1
+
+[Install]
+WantedBy=default.target
+EOT
+close($fh);
+
+isnt_enabled($random_unit);
+isnt_enabled($random_unit, user => 1);
+isnt_debian_installed($random_unit);
+isnt_debian_installed($random_unit, user => 1);
+
+#
+# "enable" creates the requested symlinks
+#
+
+unless ($ENV{'TEST_ON_REAL_SYSTEM'}) {
+    # This might exist if we don't start from a fresh directory
+    ok(! -d '/etc/systemd/user/default.target.wants',
+       'default.target.wants does not exist yet');
+}
+
+my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+is($retval, 0, "enable command succeeded");
+my $symlink_path = "/etc/systemd/user/default.target.wants/$random_unit";
+ok(-l $symlink_path, "$random_unit was enabled");
+is(readlink($symlink_path), $servicefile_path,
+    "symlink points to $servicefile_path");
+
+#
+# "is-enabled" now returns true for the user instance
+#
+
+isnt_enabled($random_unit);
+isnt_debian_installed($random_unit);
+is_enabled($random_unit, user => 1);
+is_debian_installed($random_unit, user => 1);
+
+#
+# deleting the symlinks and running "enable" again does not re-create them
+#
+
+unlink($symlink_path);
+ok(! -l $symlink_path, 'symlink deleted');
+isnt_enabled($random_unit);
+isnt_enabled($random_unit, user => 1);
+isnt_debian_installed($random_unit);
+is_debian_installed($random_unit, user => 1);
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+is($retval, 0, "enable command succeeded");
+
+isnt_enabled($random_unit, user => 1);
+
+#
+# "disable" deletes the statefile when purging
+#
+
+my $statefile = "/var/lib/systemd/deb-systemd-user-helper-enabled/$random_unit.dsh-also";
+
+ok(-f $statefile, 'state file exists');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test _DEB_SYSTEMD_HELPER_PURGE=1 $dsh --user disable '$random_unit'");
+is($retval, 0, "disable command succeeded");
+ok(! -f $statefile, 'state file does not exist anymore after purging');
+isnt_debian_installed($random_unit);
+isnt_debian_installed($random_unit, user => 1);
+
+#
+# "enable" re-creates the symlinks after purging
+#
+
+ok(! -l $symlink_path, 'symlink does not exist yet');
+isnt_enabled($random_unit);
+isnt_enabled($random_unit, user => 1);
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+is($retval, 0, "enable command succeeded");
+
+isnt_enabled($random_unit);
+isnt_debian_installed($random_unit);
+is_enabled($random_unit, user => 1);
+is_debian_installed($random_unit, user => 1);
+
+#
+# "disable" removes the symlinks
+#
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test _DEB_SYSTEMD_HELPER_PURGE=1 $dsh --user disable '$random_unit'");
+is($retval, 0, "disable command succeeded");
+
+isnt_enabled($random_unit);
+isnt_enabled($random_unit, user => 1);
+
+#
+# "enable" re-creates the symlinks
+#
+
+ok(! -l $symlink_path, 'symlink does not exist yet');
+isnt_enabled($random_unit);
+isnt_enabled($random_unit, user => 1);
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+is($retval, 0, "enable command succeeded");
+
+isnt_enabled($random_unit);
+isnt_debian_installed($random_unit);
+is_enabled($random_unit, user => 1);
+is_debian_installed($random_unit, user => 1);
+
+#
+# "purge" works
+#
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user purge '$random_unit'");
+is($retval, 0, "purge command succeeded");
+
+isnt_enabled($random_unit, user => 1);
+isnt_debian_installed($random_unit, user => 1);
+
+#
+# "enable" re-creates the symlinks after purging
+#
+
+ok(! -l $symlink_path, 'symlink does not exist yet');
+isnt_enabled($random_unit, user => 1);
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+is($retval, 0, "enable command succeeded");
+
+is_enabled($random_unit, user => 1);
+is_debian_installed($random_unit, user => 1);
+
+#
+# "mask" (when enabled) results in the symlink pointing to /dev/null
+#
+
+my $mask_path = "/etc/systemd/user/$random_unit";
+ok(! -l $mask_path, 'mask link does not exist yet');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+is($retval, 0, "mask command succeeded");
+ok(-l $mask_path, 'mask link exists');
+is(readlink($mask_path), '/dev/null', 'service masked');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+is($retval, 0, "unmask command succeeded");
+ok(! -e $mask_path, 'mask link does not exist anymore');
+
+#
+# "mask" (when disabled) works the same way
+#
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user disable '$random_unit'");
+is($retval, 0, "disable command succeeded");
+ok(! -e $symlink_path, 'symlink no longer exists');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+is($retval, 0, "mask command succeeded");
+ok(-l $mask_path, 'mask link exists');
+is(readlink($mask_path), '/dev/null', 'service masked');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+is($retval, 0, "unmask command succeeded");
+ok(! -e $mask_path, 'symlink no longer exists');
+
+#
+# "mask" / "unmask" don't do anything when the unit is already masked
+#
+
+ok(! -l $mask_path, 'mask link does not exist yet');
+symlink('/dev/null', $mask_path);
+ok(-l $mask_path, 'mask link exists');
+is(readlink($mask_path), '/dev/null', 'service masked');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+is($retval, 0, "mask command succeeded");
+ok(-l $mask_path, 'mask link exists');
+is(readlink($mask_path), '/dev/null', 'service still masked');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+is($retval, 0, "unmask command succeeded");
+ok(-l $mask_path, 'mask link exists');
+is(readlink($mask_path), '/dev/null', 'service still masked');
+
+#
+# "mask" / "unmask" don't do anything when the user copied the .service.
+#
+
+unlink($mask_path);
+
+open($fh, '>', $mask_path);
+print $fh <<'EOT';
+[Unit]
+Description=test unit
+
+[Service]
+ExecStart=/bin/sleep 1
+
+[Install]
+WantedBy=default.target
+EOT
+close($fh);
+
+ok(-e $mask_path, 'local service file exists');
+ok(! -l $mask_path, 'local service file is not a symlink');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+isnt($retval, -1, 'deb-systemd-helper could be executed');
+ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
+is($retval >> 8, 0, 'deb-systemd-helper exited with exit code 0');
+ok(-e $mask_path, 'local service file still exists');
+ok(! -l $mask_path, 'local service file is still not a symlink');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+isnt($retval, -1, 'deb-systemd-helper could be executed');
+ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
+is($retval >> 8, 0, 'deb-systemd-helper exited with exit code 0');
+ok(-e $mask_path, 'local service file still exists');
+ok(! -l $mask_path, 'local service file is still not a symlink');
+
+unlink($mask_path);
+
+#
+# "Alias=" handling
+#
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user purge '$random_unit'");
+is($retval, 0, "purge command succeeded");
+
+open($fh, '>', $servicefile_path);
+print $fh <<'EOT';
+[Unit]
+Description=test unit
+
+[Service]
+ExecStart=/bin/sleep 1
+
+[Install]
+WantedBy=default.target
+Alias=foo\x2dtest.service
+EOT
+close($fh);
+
+isnt_enabled($random_unit, user => 1);
+isnt_enabled('foo\x2dtest.service', user => 1);
+my $alias_path = '/etc/systemd/user/foo\x2dtest.service';
+ok(! -l $alias_path, 'alias link does not exist yet');
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+is($retval, 0, "enable command succeeded");
+is(readlink($alias_path), $servicefile_path, 'correct alias link');
+is_enabled($random_unit, user => 1);
+ok(! -l $mask_path, 'mask link does not exist yet');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+is($retval, 0, "mask command succeeded");
+is(readlink($alias_path), $servicefile_path, 'correct alias link');
+is(readlink($mask_path), '/dev/null', 'service masked');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+is($retval, 0, "unmask command succeeded");
+is(readlink($alias_path), $servicefile_path, 'correct alias link');
+ok(! -l $mask_path, 'mask link does not exist any more');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user disable '$random_unit'");
+isnt_enabled($random_unit, user => 1);
+ok(! -l $alias_path, 'alias link does not exist any more');
+
+#
+# "Alias=" / "mask" with removed package (as in postrm)
+#
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user purge '$random_unit'");
+is($retval, 0, "purge command succeeded");
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+is($retval, 0, "enable command succeeded");
+is(readlink($alias_path), $servicefile_path, 'correct alias link');
+
+unlink($servicefile_path);
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+is($retval, 0, "mask command succeeded with uninstalled unit");
+is(readlink($alias_path), $servicefile_path, 'correct alias link');
+is(readlink($mask_path), '/dev/null', 'service masked');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user purge '$random_unit'");
+is($retval, 0, "purge command succeeded with uninstalled unit");
+ok(! -l $alias_path, 'alias link does not exist any more');
+is(readlink($mask_path), '/dev/null', 'service masked');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+is($retval, 0, "unmask command succeeded with uninstalled unit");
+ok(! -l $mask_path, 'mask link does not exist any more');
+
+#
+# "Alias=" to the same unit name
+#
+
+open($fh, '>', $servicefile_path);
+print $fh <<"EOT";
+[Unit]
+Description=test unit
+
+[Service]
+ExecStart=/bin/sleep 1
+
+[Install]
+WantedBy=default.target
+Alias=$random_unit
+EOT
+close($fh);
+
+isnt_enabled($random_unit, user => 1);
+isnt_enabled('foo\x2dtest.service', user => 1);
+# note that in this case $alias_path and $mask_path are identical
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+is($retval, 0, "enable command succeeded");
+is_enabled($random_unit, user => 1);
+# systemctl enable does create the alias link even if it's not needed
+#ok(! -l $mask_path, 'mask link does not exist yet');
+
+unlink($servicefile_path);
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user mask '$random_unit'");
+is($retval, 0, "mask command succeeded");
+is(readlink($mask_path), '/dev/null', 'service masked');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user unmask '$random_unit'");
+is($retval, 0, "unmask command succeeded");
+ok(! -l $mask_path, 'mask link does not exist any more');
+
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user purge '$random_unit'");
+isnt_enabled($random_unit, user => 1);
+ok(! -l $mask_path, 'mask link does not exist any more');
+
+#
+# "Alias=" without "WantedBy="
+#
+
+open($fh, '>', $servicefile_path);
+print $fh <<'EOT';
+[Unit]
+Description=test unit
+
+[Service]
+ExecStart=/bin/sleep 1
+
+[Install]
+Alias=baz\x2dtest.service
+EOT
+close($fh);
+
+isnt_enabled($random_unit, user => 1);
+isnt_enabled('baz\x2dtest.service', user => 1);
+$alias_path = '/etc/systemd/user/baz\x2dtest.service';
+ok(! -l $alias_path, 'alias link does not exist yet');
+$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh --user enable '$random_unit'");
+is($retval, 0, "enable command succeeded");
+is_enabled($random_unit, user => 1);
+ok(-l $alias_path, 'alias link does exist');
+is(readlink($alias_path), $servicefile_path, 'correct alias link');
+
+done_testing;
diff --git a/t/helpers.pm b/t/helpers.pm
index 124d543..18120b4 100644
--- a/t/helpers.pm
+++ b/t/helpers.pm
@@ -27,13 +27,14 @@ sub test_setup() {
 	# directories: mount a tmpfs on /tmp.
 	system("mount -n -t tmpfs tmpfs /tmp") == 0
 	    or BAIL_OUT("mounting tmpfs on /tmp failed: $!");
-	
+
 	my $etc_systemd = bind_mount_tmp('/etc/systemd');
-	my $lib_systemd_system = bind_mount_tmp('/lib/systemd/system');
-	my $var_lib_systemd = bind_mount_tmp('/var/lib/systemd');
+        my $lib_systemd_system = bind_mount_tmp('/lib/systemd/system');
+        my $lib_systemd_user = bind_mount_tmp('/usr/lib/systemd/user');
+        my $var_lib_systemd = bind_mount_tmp('/var/lib/systemd');
 
-	# Tell `systemctl` to do not speak with the world outside our namespace.
-	$ENV{'SYSTEMCTL_INSTALL_CLIENT_SIDE'} = '1'
+        # Tell `systemctl` to do not speak with the world outside our namespace.
+        $ENV{'SYSTEMCTL_INSTALL_CLIENT_SIDE'} = '1'
     }
 }
 
@@ -53,18 +54,20 @@ sub state_file_entries {
 my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
 
 sub _unit_check {
-    my ($unit_file, $cmd, $cb, $verb) = @_;
+    my ($cmd, $cb, $verb, $unit, %opts) = @_;
+    my $user = $opts{'user'} ? '--user' : '';
+
+    my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh $user $cmd '$unit'");
 
-    my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh $cmd '$unit_file'");
     isnt($retval, -1, 'deb-systemd-helper could be executed');
     ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
-    $cb->($retval >> 8, 0, "random unit file '$unit_file' $verb $cmd");
+    $cb->($retval >> 8, 0, "random unit file '$unit' $verb $cmd");
 }
 
-sub is_enabled { _unit_check($_[0], 'is-enabled', \&is, 'is') }
-sub isnt_enabled { _unit_check($_[0], 'is-enabled', \&isnt, 'isnt') }
+sub is_enabled { _unit_check('is-enabled', \&is, 'is', @_) }
+sub isnt_enabled { _unit_check('is-enabled', \&isnt, 'isnt', @_) }
 
-sub is_debian_installed { _unit_check($_[0], 'debian-installed', \&is, 'is') }
-sub isnt_debian_installed { _unit_check($_[0], 'debian-installed', \&isnt, 'isnt') }
+sub is_debian_installed { _unit_check('debian-installed', \&is, 'is', @_) }
+sub isnt_debian_installed { _unit_check('debian-installed', \&isnt, 'isnt', @_) }
 
 1;
-- 
2.17.0

-------------- next part --------------
From 39fe4c5330f837b0ce4c8f6f50a94d169897319e Mon Sep 17 00:00:00 2001
From: Daniele Nicolodi <daniele at grinta.net>
Date: Sat, 2 Jun 2018 16:43:23 -0600
Subject: [PATCH 08/10] deb-systemd-helper: Implement user instance's service
 handling

---
 script/deb-systemd-helper | 56 +++++++++++++++++++++++++++------------
 1 file changed, 39 insertions(+), 17 deletions(-)

diff --git a/script/deb-systemd-helper b/script/deb-systemd-helper
index e419cc4..4f75ffd 100755
--- a/script/deb-systemd-helper
+++ b/script/deb-systemd-helper
@@ -90,9 +90,17 @@ use Getopt::Long; # in core since Perl 5
 # only have perl-base, not perl).
 eval { require Data::Dumper; } or *Data::Dumper::Dumper = sub { "no Data::Dumper" };
 
+use constant {
+     SYSTEM_INSTANCE_ENABLED_STATE_DIR => '/var/lib/systemd/deb-systemd-helper-enabled',
+     USER_INSTANCE_ENABLED_STATE_DIR   => '/var/lib/systemd/deb-systemd-user-helper-enabled',
+     SYSTEM_INSTANCE_MASKED_STATE_DIR  => '/var/lib/systemd/deb-systemd-helper-masked',
+     USER_INSTANCE_MASKED_STATE_DIR    => '/var/lib/systemd/deb-systemd-user-helper-masked',
+};
+
 my $quiet = 0;
-my $enabled_state_dir = '/var/lib/systemd/deb-systemd-helper-enabled';
-my $masked_state_dir = '/var/lib/systemd/deb-systemd-helper-masked';
+my $instance = 'system';
+my $enabled_state_dir = SYSTEM_INSTANCE_ENABLED_STATE_DIR;
+my $masked_state_dir = SYSTEM_INSTANCE_MASKED_STATE_DIR;
 
 # Globals are bad, but in this specific case, it really makes things much
 # easier to write and understand.
@@ -118,11 +126,15 @@ sub find_unit {
     my ($scriptname) = @_;
 
     my $service_path = $scriptname;
-    if (-f "/etc/systemd/system/$scriptname") {
-        $service_path = "/etc/systemd/system/$scriptname";
-    } elsif (-f "/lib/systemd/system/$scriptname") {
-        $service_path = "/lib/systemd/system/$scriptname";
+
+    if (-f "/etc/systemd/$instance/$scriptname") {
+        $service_path = "/etc/systemd/$instance/$scriptname";
+    } elsif (-f "/lib/systemd/$instance/$scriptname") {
+        $service_path = "/lib/systemd/$instance/$scriptname";
+    } elsif (-f "/usr/lib/systemd/$instance/$scriptname") {
+        $service_path = "/usr/lib/systemd/$instance/$scriptname";
     }
+
     return $service_path;
 }
 
@@ -195,7 +207,7 @@ sub get_link_closure {
         if ($line =~ /^\s*(WantedBy|RequiredBy)=(.+)$/i) {
             for my $value (split(/\s+/, $2)) {
                 $value =~ s/^(["'])(.*)\g1$/$2/;
-                my $wants_dir = "/etc/systemd/system/$value";
+                my $wants_dir = "/etc/systemd/$instance/$value";
                 $wants_dir .= '.wants' if $1 eq 'WantedBy';
                 $wants_dir .= '.requires' if $1 eq 'RequiredBy';
                 push @links, { dest => $service_path, src => "$wants_dir/$scriptname" };
@@ -215,7 +227,7 @@ sub get_link_closure {
             for my $value (split(/\s+/, $1)) {
                 $value =~ s/^(["'])(.*)\g1$/$2/;
                 if ($value ne $unit_name) {
-                    push @links, { dest => $service_path, src => "/etc/systemd/system/$1" };
+                    push @links, { dest => $service_path, src => "/etc/systemd/$instance/$1" };
                 }
             }
         }
@@ -245,7 +257,7 @@ sub no_link_installed {
 
 sub enable {
     my ($scriptname, $service_path) = @_;
-    if ($has_systemctl) {
+    if ($has_systemctl && $instance eq 'system') {
         # We use systemctl preset on the initial installation only
         # On upgrade, we manually add the missing symlinks only if
         # the service already has some links installed.
@@ -279,7 +291,7 @@ sub make_systemd_links {
         record_in_statefile($dsh_state, $service_link);
 
         my $statefile = $service_link;
-        $statefile =~ s,^/etc/systemd/system/,$enabled_state_dir/,;
+        $statefile =~ s,^/etc/systemd/$instance/,$enabled_state_dir/,;
         next if -e $statefile;
 
         if ($opts{'create_links'} && ! -l $service_link) {
@@ -378,7 +390,7 @@ sub remove_links {
         #   but not re-created when re-installing the package.
         if (is_purge() || -l $link) {
             my $link_state = $link;
-            $link_state =~ s,^/etc/systemd/system/,$enabled_state_dir/,;
+            $link_state =~ s,^/etc/systemd/$instance/,$enabled_state_dir/,;
             unlink($link_state);
         }
 
@@ -425,7 +437,7 @@ sub rmdir_if_empty {
 sub mask_service {
     my ($scriptname, $service_path) = @_;
 
-    my $mask_link = '/etc/systemd/system/' . basename($service_path);
+    my $mask_link = "/etc/systemd/$instance/" . basename($service_path);
 
     if (-e $mask_link) {
         # If the link already exists, don’t do anything.
@@ -447,7 +459,7 @@ sub mask_service {
     $changed_sth = 1;
 
     my $statefile = $mask_link;
-    $statefile =~ s,^/etc/systemd/system/,$masked_state_dir/,;
+    $statefile =~ s,^/etc/systemd/$instance/,$masked_state_dir/,;
 
     # Store the fact that we masked this service, so that we can unmask it on
     # installation time. We cannot unconditionally unmask because that would
@@ -460,7 +472,7 @@ sub mask_service {
 sub unmask_service {
     my ($scriptname, $service_path) = @_;
 
-    my $mask_link = '/etc/systemd/system/' . basename($service_path);
+    my $mask_link = "/etc/systemd/$instance/" . basename($service_path);
 
     # Not masked? Nothing to do.
     return unless -e $mask_link;
@@ -471,7 +483,7 @@ sub unmask_service {
     }
 
     my $statefile = $mask_link;
-    $statefile =~ s,^/etc/systemd/system/,$masked_state_dir/,;
+    $statefile =~ s,^/etc/systemd/$instance/,$masked_state_dir/,;
 
     if (! -e $statefile) {
         debug "Not unmasking $mask_link because the state file $statefile does not exist";
@@ -486,8 +498,16 @@ sub unmask_service {
 
 my $result = GetOptions(
     "quiet" => \$quiet,
+    "user" => sub { $instance = 'user'; },
+    "system" => sub { $instance = 'system'; }, # default
 );
 
+if ($instance eq 'user') {
+    debug "is user unit = yes";
+    $enabled_state_dir = USER_INSTANCE_ENABLED_STATE_DIR;
+    $masked_state_dir = USER_INSTANCE_MASKED_STATE_DIR;
+}
+
 my $action = shift;
 if (!defined($action)) {
     # Called without arguments. Explain that this script should not be run interactively.
@@ -557,12 +577,14 @@ for my $scriptname (@ARGV) {
         remove_links($service_path);
         # Clean up the state dir if it’s empty, or at least clean up all empty
         # subdirectories. Necessary to cleanly pass a piuparts run.
-        rmdir_if_empty('/var/lib/systemd/deb-systemd-helper-enabled');
+        rmdir_if_empty(SYSTEM_INSTANCE_ENABLED_STATE_DIR);
+        rmdir_if_empty(USER_INSTANCE_ENABLED_STATE_DIR);
 
         # Same with directories below /etc/systemd, where we create symlinks.
         # If systemd is not installed (and no other package shipping service
         # files), this would make piuparts fail, too.
         rmdir_if_empty($_) for (grep { -d } </etc/systemd/system/*>);
+        rmdir_if_empty($_) for (grep { -d } </etc/systemd/user/*>);
     }
 
     if ($action eq 'enable') {
@@ -584,7 +606,7 @@ for my $scriptname (@ARGV) {
 # 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") {
+if ($changed_sth && $instance eq 'system' && -d "/run/systemd/system") {
     system("systemctl", "daemon-reload");
 }
 
-- 
2.17.0

-------------- next part --------------
From 7ebbe2db2225fde09a76c8a62a830ccc5b5579d7 Mon Sep 17 00:00:00 2001
From: Daniele Nicolodi <daniele at grinta.net>
Date: Sat, 2 Jun 2018 15:42:41 -0600
Subject: [PATCH 07/10] autopkgtests: Drop seteup steps that are not required
 anymore

---
 debian/tests/control | 1 -
 debian/tests/t       | 3 ---
 2 files changed, 4 deletions(-)

diff --git a/debian/tests/control b/debian/tests/control
index d85b9f4..3883872 100644
--- a/debian/tests/control
+++ b/debian/tests/control
@@ -1,5 +1,4 @@
 Tests: t
 Depends: @,
-  cpanminus,
   build-essential
 Restrictions: needs-root, breaks-testbed, allow-stderr, isolation-container
diff --git a/debian/tests/t b/debian/tests/t
index 29e4b66..1f8476c 100644
--- a/debian/tests/t
+++ b/debian/tests/t
@@ -1,9 +1,6 @@
 #!/bin/sh
 set -eu
 
-mount --make-rprivate /
-cpanm Linux::Clone
-
 export TEST_ON_REAL_SYSTEM=1
 
 for test in t/*.t; do
-- 
2.17.0

-------------- next part --------------
From bf5e96bf02dc086e35760b299cfb814ebdfe6f48 Mon Sep 17 00:00:00 2001
From: Daniele Nicolodi <daniele at grinta.net>
Date: Sat, 2 Jun 2018 15:38:03 -0600
Subject: [PATCH 06/10] deb-systemd-helper: Fix typo in man page

---
 script/deb-systemd-helper | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/script/deb-systemd-helper b/script/deb-systemd-helper
index 3d40d8d..e419cc4 100755
--- a/script/deb-systemd-helper
+++ b/script/deb-systemd-helper
@@ -43,7 +43,7 @@ B<deb-systemd-helper> is a Debian-specific helper script which re-implements
 the enable, disable, is-enabled and reenable commands from systemctl.
 
 The "enable" action will only be performed once (when first installing the
-package). On the first "enable", an state file is created which will be deleted
+package). On the first "enable", a state file is created which will be deleted
 upon "purge".
 
 The "mask" action will keep state on whether the service was enabled/disabled
-- 
2.17.0

-------------- next part --------------
From 4ef70a0d794ae1d3c9864c359bee405a5a95c7cd Mon Sep 17 00:00:00 2001
From: Daniele Nicolodi <daniele at grinta.net>
Date: Sat, 2 Jun 2018 15:34:43 -0600
Subject: [PATCH 05/10] tests: Move some more common functions to helpers.pm

Unify their implementation in the process.
---
 t/001-deb-systemd-helper.t         | 15 ---------------
 t/002-deb-systemd-helper-update.t  | 25 -------------------------
 t/003-deb-systemd-helper-complex.t | 15 ---------------
 t/helpers.pm                       | 30 ++++++++++++++++++++++++++++++
 4 files changed, 30 insertions(+), 55 deletions(-)

diff --git a/t/001-deb-systemd-helper.t b/t/001-deb-systemd-helper.t
index 5f507e6..a2c96c3 100644
--- a/t/001-deb-systemd-helper.t
+++ b/t/001-deb-systemd-helper.t
@@ -16,21 +16,6 @@ test_setup();
 
 my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
 
-sub _unit_check {
-    my ($unit_file, $cmd, $cb, $verb) = @_;
-
-    my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh $cmd '$unit_file'");
-    isnt($retval, -1, 'deb-systemd-helper could be executed');
-    ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
-    $cb->($retval >> 8, 0, "random unit file $verb $cmd");
-}
-
-sub is_enabled { _unit_check($_[0], 'is-enabled', \&is, 'is') }
-sub isnt_enabled { _unit_check($_[0], 'is-enabled', \&isnt, 'isnt') }
-
-sub is_debian_installed { _unit_check($_[0], 'debian-installed', \&is, 'is') }
-sub isnt_debian_installed { _unit_check($_[0], 'debian-installed', \&isnt, 'isnt') }
-
 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 # ┃ Verify “is-enabled” is not true for a random, non-existing unit file.     ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
diff --git a/t/002-deb-systemd-helper-update.t b/t/002-deb-systemd-helper-update.t
index ad4606e..647002f 100644
--- a/t/002-deb-systemd-helper-update.t
+++ b/t/002-deb-systemd-helper-update.t
@@ -16,31 +16,6 @@ test_setup();
 
 my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
 
-# reads in a whole file
-sub slurp {
-    open my $fh, '<', shift;
-    local $/;
-    <$fh>;
-}
-
-sub state_file_entries {
-    my ($path) = @_;
-    my $bytes = slurp($path);
-    return split("\n", $bytes);
-}
-
-sub _unit_enabled {
-    my ($unit_file, $cb, $verb) = @_;
-
-    my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh is-enabled $unit_file");
-    isnt($retval, -1, 'deb-systemd-helper could be executed');
-    ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
-    $cb->($retval >> 8, 0, "random unit file $verb enabled");
-}
-
-sub is_enabled { _unit_enabled($_[0], \&is, 'is') }
-sub isnt_enabled { _unit_enabled($_[0], \&isnt, 'isnt') }
-
 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 # ┃ Verify “is-enabled” is not true for a random, non-existing unit file.     ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
diff --git a/t/003-deb-systemd-helper-complex.t b/t/003-deb-systemd-helper-complex.t
index 46d5ad7..589b9bf 100644
--- a/t/003-deb-systemd-helper-complex.t
+++ b/t/003-deb-systemd-helper-complex.t
@@ -16,21 +16,6 @@ test_setup();
 
 my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
 
-sub _unit_check {
-    my ($unit_file, $cmd, $cb, $verb) = @_;
-
-    my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh $cmd $unit_file");
-    isnt($retval, -1, 'deb-systemd-helper could be executed');
-    ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
-    $cb->($retval >> 8, 0, "random unit file $unit_file $verb $cmd");
-}
-
-sub is_enabled { _unit_check($_[0], 'is-enabled', \&is, 'is') }
-sub isnt_enabled { _unit_check($_[0], 'is-enabled', \&isnt, 'isnt') }
-
-sub is_debian_installed { _unit_check($_[0], 'debian-installed', \&is, 'is') }
-sub isnt_debian_installed { _unit_check($_[0], 'debian-installed', \&isnt, 'isnt') }
-
 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 # ┃ Create two unit files with random names; one refers to the other (Also=). ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
diff --git a/t/helpers.pm b/t/helpers.pm
index b6c7603..124d543 100644
--- a/t/helpers.pm
+++ b/t/helpers.pm
@@ -37,4 +37,34 @@ sub test_setup() {
     }
 }
 
+# reads in a whole file
+sub slurp {
+    open my $fh, '<', shift;
+    local $/;
+    <$fh>;
+}
+
+sub state_file_entries {
+    my ($path) = @_;
+    my $bytes = slurp($path);
+    return split("\n", $bytes);
+}
+
+my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
+
+sub _unit_check {
+    my ($unit_file, $cmd, $cb, $verb) = @_;
+
+    my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh $cmd '$unit_file'");
+    isnt($retval, -1, 'deb-systemd-helper could be executed');
+    ok(!($retval & 127), 'deb-systemd-helper did not exit due to a signal');
+    $cb->($retval >> 8, 0, "random unit file '$unit_file' $verb $cmd");
+}
+
+sub is_enabled { _unit_check($_[0], 'is-enabled', \&is, 'is') }
+sub isnt_enabled { _unit_check($_[0], 'is-enabled', \&isnt, 'isnt') }
+
+sub is_debian_installed { _unit_check($_[0], 'debian-installed', \&is, 'is') }
+sub isnt_debian_installed { _unit_check($_[0], 'debian-installed', \&isnt, 'isnt') }
+
 1;
-- 
2.17.0

-------------- next part --------------
From 1151003df787bc7fbed5a74beda991e08214ccce Mon Sep 17 00:00:00 2001
From: Daniele Nicolodi <daniele at grinta.net>
Date: Sat, 2 Jun 2018 15:20:43 -0600
Subject: [PATCH 04/10] tests: Make sure that the tests do not clutter the host
 system

In the mount namespace created for the tests, remount the root
filesystem read-only. To be able to create temporary files and
directories, mount a tmpfs on /tmp.
---
 t/helpers.pm | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/t/helpers.pm b/t/helpers.pm
index 348316d..b6c7603 100644
--- a/t/helpers.pm
+++ b/t/helpers.pm
@@ -17,6 +17,16 @@ sub test_setup() {
 
 	my $retval = Linux::Clone::unshare Linux::Clone::NEWNS;
 	BAIL_OUT("Cannot unshare(NEWNS): $!") if $retval != 0;
+
+	# Make sure that the tests do not clutter the system by
+	# remounting the root filesystem read-only.
+	system("mount -n -o bind,ro / /") == 0
+	    or BAIL_OUT("bind-mounting / read-only failed: $!");
+
+	# We still need to be able to create temporary files and
+	# directories: mount a tmpfs on /tmp.
+	system("mount -n -t tmpfs tmpfs /tmp") == 0
+	    or BAIL_OUT("mounting tmpfs on /tmp failed: $!");
 	
 	my $etc_systemd = bind_mount_tmp('/etc/systemd');
 	my $lib_systemd_system = bind_mount_tmp('/lib/systemd/system');
-- 
2.17.0

-------------- next part --------------
From 9e77bb01d736dba84bc4efa5179af55f135311d3 Mon Sep 17 00:00:00 2001
From: Daniele Nicolodi <daniele at grinta.net>
Date: Sat, 2 Jun 2018 15:10:58 -0600
Subject: [PATCH 03/10] tests: Do not require Linux::Clone Perl module if
 TEST_ON_REAL_SYSTEM is set

If the TEST_ON_REAL_SYSTEM environment variable is set, the bind
mounting of empty directories not top of system difrectoried affected
by the tests is skipt. Therefore, there is no need to isolate the
tests in a mount namespace.
---
 t/helpers.pm | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/t/helpers.pm b/t/helpers.pm
index 548be36..348316d 100644
--- a/t/helpers.pm
+++ b/t/helpers.pm
@@ -1,8 +1,4 @@
 use File::Temp qw(tempdir); # in core since perl 5.6.1
-use Linux::Clone; # neither in core nor in Debian :-/
-
-my $retval = Linux::Clone::unshare Linux::Clone::NEWNS;
-BAIL_OUT("Cannot unshare(NEWNS): $!") if $retval != 0;
 
 sub bind_mount_tmp {
     my ($dir) = @_;
@@ -17,6 +13,11 @@ sub bind_mount_tmp {
 # actual locations and code paths.
 sub test_setup() {
     unless ($ENV{'TEST_ON_REAL_SYSTEM'}) {
+	use Linux::Clone; # neither in core nor in Debian :-/
+
+	my $retval = Linux::Clone::unshare Linux::Clone::NEWNS;
+	BAIL_OUT("Cannot unshare(NEWNS): $!") if $retval != 0;
+	
 	my $etc_systemd = bind_mount_tmp('/etc/systemd');
 	my $lib_systemd_system = bind_mount_tmp('/lib/systemd/system');
 	my $var_lib_systemd = bind_mount_tmp('/var/lib/systemd');
-- 
2.17.0

-------------- next part --------------
From b273624eedda5d54e447f4394b7eb7194a1834b8 Mon Sep 17 00:00:00 2001
From: Daniele Nicolodi <daniele at grinta.net>
Date: Sat, 2 Jun 2018 15:09:56 -0600
Subject: [PATCH 02/10] tests: Fix setup when TEST_ON_REAL_SYSTEM is not set

---
 t/helpers.pm | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/t/helpers.pm b/t/helpers.pm
index b8272a5..548be36 100644
--- a/t/helpers.pm
+++ b/t/helpers.pm
@@ -13,13 +13,16 @@ sub bind_mount_tmp {
 }
 
 # In a new mount namespace, bindmount tmpdirs on /etc/systemd and
-# /var/lib/systemd to start with clean directories yet use the actual
-# locations and code paths.
+# /lib/systemd/system to start with clean directories yet use the
+# actual locations and code paths.
 sub test_setup() {
     unless ($ENV{'TEST_ON_REAL_SYSTEM'}) {
 	my $etc_systemd = bind_mount_tmp('/etc/systemd');
-	my $lib_systemd = bind_mount_tmp('/lib/systemd');
+	my $lib_systemd_system = bind_mount_tmp('/lib/systemd/system');
 	my $var_lib_systemd = bind_mount_tmp('/var/lib/systemd');
+
+	# Tell `systemctl` to do not speak with the world outside our namespace.
+	$ENV{'SYSTEMCTL_INSTALL_CLIENT_SIDE'} = '1'
     }
 }
 
-- 
2.17.0

-------------- next part --------------
From 4368c53fcdc794c7509a5e802057e3fdaa6a7e09 Mon Sep 17 00:00:00 2001
From: Daniele Nicolodi <daniele at grinta.net>
Date: Sat, 2 Jun 2018 15:05:00 -0600
Subject: [PATCH 01/10] tests: Move common setup code to a new helpers.pm
 module

---
 t/001-deb-systemd-helper.t         | 29 +++++------------------------
 t/002-deb-systemd-helper-update.t  | 29 +++++------------------------
 t/003-deb-systemd-helper-complex.t | 29 +++++------------------------
 t/helpers.pm                       | 26 ++++++++++++++++++++++++++
 t/helpers.pm~                      |  1 +
 5 files changed, 42 insertions(+), 72 deletions(-)
 create mode 100644 t/helpers.pm
 create mode 100644 t/helpers.pm~

diff --git a/t/001-deb-systemd-helper.t b/t/001-deb-systemd-helper.t
index 99ee52d..5f507e6 100644
--- a/t/001-deb-systemd-helper.t
+++ b/t/001-deb-systemd-helper.t
@@ -8,13 +8,11 @@ use File::Temp qw(tempfile tempdir); # in core since perl 5.6.1
 use File::Path qw(make_path); # in core since Perl 5.001
 use File::Basename; # in core since Perl 5
 use FindBin; # in core since Perl 5.00307
-use Linux::Clone; # neither in core nor in Debian :-/
 
-# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-# ┃ SETUP: in a new mount namespace, bindmount tmpdirs on /etc/systemd and    ┃
-# ┃ /var/lib/systemd to start with clean directories yet use the actual       ┃
-# ┃ locations and code paths.                                                 ┃
-# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+use lib "$FindBin::Bin/.";
+use helpers;
+
+test_setup();
 
 my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
 
@@ -33,23 +31,6 @@ sub isnt_enabled { _unit_check($_[0], 'is-enabled', \&isnt, 'isnt') }
 sub is_debian_installed { _unit_check($_[0], 'debian-installed', \&is, 'is') }
 sub isnt_debian_installed { _unit_check($_[0], 'debian-installed', \&isnt, 'isnt') }
 
-my $retval = Linux::Clone::unshare Linux::Clone::NEWNS;
-BAIL_OUT("Cannot unshare(NEWNS): $!") if $retval != 0;
-
-sub bind_mount_tmp {
-    my ($dir) = @_;
-    my $tmp = tempdir(CLEANUP => 1);
-    system("mount -n --bind $tmp $dir") == 0
-        or BAIL_OUT("bind-mounting $tmp to $dir failed: $!");
-    return $tmp;
-}
-
-unless ($ENV{'TEST_ON_REAL_SYSTEM'}) {
-    my $etc_systemd = bind_mount_tmp('/etc/systemd');
-    my $lib_systemd = bind_mount_tmp('/lib/systemd');
-    my $var_lib_systemd = bind_mount_tmp('/var/lib/systemd');
-}
-
 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 # ┃ Verify “is-enabled” is not true for a random, non-existing unit file.     ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
@@ -96,7 +77,7 @@ unless ($ENV{'TEST_ON_REAL_SYSTEM'}) {
        'multi-user.target.wants does not exist yet');
 }
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable '$random_unit'");
+my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable '$random_unit'");
 is($retval, 0, "enable command succeeded");
 my $symlink_path = "/etc/systemd/system/multi-user.target.wants/$random_unit";
 ok(-l $symlink_path, "$random_unit was enabled");
diff --git a/t/002-deb-systemd-helper-update.t b/t/002-deb-systemd-helper-update.t
index d16acd9..ad4606e 100644
--- a/t/002-deb-systemd-helper-update.t
+++ b/t/002-deb-systemd-helper-update.t
@@ -8,13 +8,11 @@ use File::Temp qw(tempfile tempdir); # in core since perl 5.6.1
 use File::Path qw(make_path); # in core since Perl 5.001
 use File::Basename; # in core since Perl 5
 use FindBin; # in core since Perl 5.00307
-use Linux::Clone; # neither in core nor in Debian :-/
 
-# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-# ┃ SETUP: in a new mount namespace, bindmount tmpdirs on /etc/systemd and    ┃
-# ┃ /var/lib/systemd to start with clean directories yet use the actual       ┃
-# ┃ locations and code paths.                                                 ┃
-# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+use lib "$FindBin::Bin/.";
+use helpers;
+
+test_setup();
 
 my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
 
@@ -43,23 +41,6 @@ sub _unit_enabled {
 sub is_enabled { _unit_enabled($_[0], \&is, 'is') }
 sub isnt_enabled { _unit_enabled($_[0], \&isnt, 'isnt') }
 
-my $retval = Linux::Clone::unshare Linux::Clone::NEWNS;
-BAIL_OUT("Cannot unshare(NEWNS): $!") if $retval != 0;
-
-sub bind_mount_tmp {
-    my ($dir) = @_;
-    my $tmp = tempdir(CLEANUP => 1);
-    system("mount -n --bind $tmp $dir") == 0
-        or BAIL_OUT("bind-mounting $tmp to $dir failed: $!");
-    return $tmp;
-}
-
-unless ($ENV{'TEST_ON_REAL_SYSTEM'}) {
-    my $etc_systemd = bind_mount_tmp('/etc/systemd');
-    my $lib_systemd = bind_mount_tmp('/lib/systemd');
-    my $var_lib_systemd = bind_mount_tmp('/var/lib/systemd');
-}
-
 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 # ┃ Verify “is-enabled” is not true for a random, non-existing unit file.     ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
@@ -91,7 +72,7 @@ close($fh);
 # ┃ Verify “enable” creates the requested symlinks.                           ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable $random_unit");
+my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable $random_unit");
 my $symlink_path = "/etc/systemd/system/multi-user.target.wants/$random_unit";
 ok(-l $symlink_path, "$random_unit was enabled");
 is(readlink($symlink_path), $servicefile_path,
diff --git a/t/003-deb-systemd-helper-complex.t b/t/003-deb-systemd-helper-complex.t
index 4bcf4be..46d5ad7 100644
--- a/t/003-deb-systemd-helper-complex.t
+++ b/t/003-deb-systemd-helper-complex.t
@@ -8,13 +8,11 @@ use File::Temp qw(tempfile tempdir); # in core since perl 5.6.1
 use File::Path qw(make_path); # in core since Perl 5.001
 use File::Basename; # in core since Perl 5
 use FindBin; # in core since Perl 5.00307
-use Linux::Clone; # neither in core nor in Debian :-/
 
-# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-# ┃ SETUP: in a new mount namespace, bindmount tmpdirs on /etc/systemd and    ┃
-# ┃ /var/lib/systemd to start with clean directories yet use the actual       ┃
-# ┃ locations and code paths.                                                 ┃
-# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+use lib "$FindBin::Bin/.";
+use helpers;
+
+test_setup();
 
 my $dsh = "$FindBin::Bin/../script/deb-systemd-helper";
 
@@ -33,23 +31,6 @@ sub isnt_enabled { _unit_check($_[0], 'is-enabled', \&isnt, 'isnt') }
 sub is_debian_installed { _unit_check($_[0], 'debian-installed', \&is, 'is') }
 sub isnt_debian_installed { _unit_check($_[0], 'debian-installed', \&isnt, 'isnt') }
 
-my $retval = Linux::Clone::unshare Linux::Clone::NEWNS;
-BAIL_OUT("Cannot unshare(NEWNS): $!") if $retval != 0;
-
-sub bind_mount_tmp {
-    my ($dir) = @_;
-    my $tmp = tempdir(CLEANUP => 1);
-    system("mount -n --bind $tmp $dir") == 0
-        or BAIL_OUT("bind-mounting $tmp to $dir failed: $!");
-    return $tmp;
-}
-
-unless ($ENV{'TEST_ON_REAL_SYSTEM'}) {
-    my $etc_systemd = bind_mount_tmp('/etc/systemd');
-    my $lib_systemd = bind_mount_tmp('/lib/systemd');
-    my $var_lib_systemd = bind_mount_tmp('/var/lib/systemd');
-}
-
 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 # ┃ Create two unit files with random names; one refers to the other (Also=). ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
@@ -114,7 +95,7 @@ unless ($ENV{'TEST_ON_REAL_SYSTEM'}) {
        'multi-user.target.wants does not exist yet');
 }
 
-$retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable $random_unit1");
+my $retval = system("DPKG_MAINTSCRIPT_PACKAGE=test $dsh enable $random_unit1");
 my %links = map { (basename($_), readlink($_)) }
     ("/etc/systemd/system/multi-user.target.wants/$random_unit1",
      "/etc/systemd/system/multi-user.target.wants/$random_unit2");
diff --git a/t/helpers.pm b/t/helpers.pm
new file mode 100644
index 0000000..b8272a5
--- /dev/null
+++ b/t/helpers.pm
@@ -0,0 +1,26 @@
+use File::Temp qw(tempdir); # in core since perl 5.6.1
+use Linux::Clone; # neither in core nor in Debian :-/
+
+my $retval = Linux::Clone::unshare Linux::Clone::NEWNS;
+BAIL_OUT("Cannot unshare(NEWNS): $!") if $retval != 0;
+
+sub bind_mount_tmp {
+    my ($dir) = @_;
+    my $tmp = tempdir(CLEANUP => 1);
+    system("mount -n --bind $tmp $dir") == 0
+        or BAIL_OUT("bind-mounting $tmp to $dir failed: $!");
+    return $tmp;
+}
+
+# In a new mount namespace, bindmount tmpdirs on /etc/systemd and
+# /var/lib/systemd to start with clean directories yet use the actual
+# locations and code paths.
+sub test_setup() {
+    unless ($ENV{'TEST_ON_REAL_SYSTEM'}) {
+	my $etc_systemd = bind_mount_tmp('/etc/systemd');
+	my $lib_systemd = bind_mount_tmp('/lib/systemd');
+	my $var_lib_systemd = bind_mount_tmp('/var/lib/systemd');
+    }
+}
+
+1;
diff --git a/t/helpers.pm~ b/t/helpers.pm~
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/t/helpers.pm~
@@ -0,0 +1 @@
+
-- 
2.17.0



More information about the Pkg-systemd-maintainers mailing list