[systemd-devel] [PATCH] sysv-generator: Replace Provides: symlinks with real units

Zbigniew Jędrzejewski-Szmek zbyszek at in.waw.pl
Wed Jan 21 07:36:26 PST 2015


On Wed, Jan 21, 2015 at 10:46:03AM +0100, Martin Pitt wrote:
> Keeping track of which alias symlinks we actually want is error prone, and
> restricting the creation of services for enabled init.d scripts would reduce
> the utility of the generator (for manual starting disabled init.d scripts) as
> well as not cover the second case. So if we encounter an existing symlink, just
> remove it before writing a real unit.
Looks fine. Although the code is clearer than the description :)

> Note that two init.d scripts "foo" and "bar" which both provide the same name
> "common" already work. The first processed init script wins and creates the
> "common.service" symlink, and the second just fails to create the symlink
> again. Thus create an additional test case for this to ensure that it keeps
> working sensibly.
> 
> https://bugs.debian.org/775404
> ---
>  src/sysv-generator/sysv-generator.c | 12 ++++++++++++
>  test/sysv-generator-test.py         | 38 +++++++++++++++++++++++++++++++++++++
>  2 files changed, 50 insertions(+)
> 
> diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
> index a47b072..fd3ee20 100644
> --- a/src/sysv-generator/sysv-generator.c
> +++ b/src/sysv-generator/sysv-generator.c
> @@ -147,6 +147,7 @@ static int generate_unit_file(SysvStub *s) {
>          _cleanup_free_ char *wants = NULL;
>          _cleanup_free_ char *conflicts = NULL;
>          int r;
> +        struct stat st;
>  
>          before = strv_join(s->before, " ");
>          if (!before)
> @@ -168,6 +169,14 @@ static int generate_unit_file(SysvStub *s) {
>          if (!unit)
>                  return log_oom();
>  
> +        /* We might already have a symlink with the same name from a Provides:,
> +         * or from backup files like /etc/init.d/foo.bak. Real scripts always win,
> +         * so remove an existing link */
> +        if (lstat(unit, &st) == 0 && S_ISLNK(st.st_mode)) {
> +                log_warning("Overwriting existing symlink %s with real service", unit);
> +                unlink(unit);
> +        }
> +
>          f = fopen(unit, "wxe");
>          if (!f)
>                  return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
> @@ -343,6 +352,8 @@ static int load_sysv(SysvStub *s) {
>          if (!f)
>                  return errno == ENOENT ? 0 : -errno;
>  
> +        log_debug("loading SysV script %s", s->path);
Capital "L"?

> +
>          while (!feof(f)) {
>                  char l[LINE_MAX], *t;
>  
> @@ -492,6 +503,7 @@ static int load_sysv(SysvStub *s) {
>                                                  continue;
>  
>                                          if (unit_name_to_type(m) == UNIT_SERVICE) {
> +                                                log_debug("Adding Provides: alias '%s' for '%s'", m, s->name);
>                                                  r = add_alias(s->name, m);
>                                          } else {
>                                                  /* NB: SysV targets
> diff --git a/test/sysv-generator-test.py b/test/sysv-generator-test.py
> index a3daa9f..63a10ec 100644
> --- a/test/sysv-generator-test.py
> +++ b/test/sysv-generator-test.py
> @@ -271,6 +271,25 @@ class SysvGeneratorTest(unittest.TestCase):
>              self.assertEqual(os.readlink(os.path.join(self.out_dir, f)),
>                               'foo.service')
>  
> +    def test_same_provides_in_multiple_scripts(self):
> +        '''multiple init.d scripts provide the same name'''
> +
> +        self.add_sysv('foo', {'Provides': 'foo common'}, enable=True, prio=1)
> +        self.add_sysv('bar', {'Provides': 'bar common'}, enable=True, prio=2)
> +        err, results = self.run_generator()
> +        self.assertEqual(sorted(results), ['bar.service', 'foo.service'])
> +        # should create symlink for the alternative name for either unit
> +        self.assertIn(os.readlink(os.path.join(self.out_dir, 'common.service')),
> +                      ['foo.service', 'bar.service'])
> +
> +    def test_provide_other_script(self):
> +        '''init.d scripts provides the name of another init.d script'''
> +
> +        self.add_sysv('foo', {'Provides': 'foo bar'}, enable=True)
> +        self.add_sysv('bar', {'Provides': 'bar'}, enable=True)
> +        err, results = self.run_generator()
> +        self.assertEqual(sorted(results), ['bar.service', 'foo.service'])
> +
>      def test_nonexecutable_script(self):
>          '''ignores non-executable init.d script'''
>  
> @@ -313,6 +332,25 @@ class SysvGeneratorTest(unittest.TestCase):
>          self.assertEqual(os.readlink(os.path.join(self.out_dir, 'bar.service')),
>                           'foo.service')
>  
> +    def test_backup_file(self):
> +        '''init.d script with backup file'''
> +
> +        script = self.add_sysv('foo', {}, enable=True)
> +        # backup files (not enabled in rcN.d/)
> +        shutil.copy(script, script + '.bak')
> +        shutil.copy(script, script + '.old')
> +
> +        err, results = self.run_generator()
> +        print(err)
> +        self.assertEqual(sorted(results),
> +                         ['foo.bak.service', 'foo.old.service', 'foo.service'])
> +
> +        # ensure we don't try to create a symlink to itself
> +        self.assertNotIn(err, 'itself')
> +
> +        self.assert_enabled('foo.service', [2, 3, 4, 5])
> +        self.assert_enabled('foo.bak.service', [])
> +        self.assert_enabled('foo.old.service', [])
Looks fine from my POV.

Zbyszek


More information about the systemd-devel mailing list