sysv-generator: do not join dependencies on one line, split them
authorLukas Nykryn <lnykryn@redhat.com>
Wed, 20 Jan 2016 14:16:32 +0000 (15:16 +0100)
committerLukas Nykryn <lnykryn@redhat.com>
Thu, 21 Jan 2016 11:53:14 +0000 (12:53 +0100)
If there is a lot of initscripts and dependencies between them we might
end generating After= (and similar) lines which are longer then LINE_MAX
and thus rejected by parser in systemd.

Fixes #2099

src/sysv-generator/sysv-generator.c
test/sysv-generator-test.py

index 5075548..d48d5ab 100644 (file)
@@ -161,7 +161,6 @@ static int add_alias(const char *service, const char *alias) {
 }
 
 static int generate_unit_file(SysvStub *s) {
-        _cleanup_free_ char *before = NULL, *after = NULL, *wants = NULL, *conflicts = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         const char *unit;
         char **p;
@@ -174,14 +173,6 @@ static int generate_unit_file(SysvStub *s) {
 
         unit = strjoina(arg_dest, "/", s->name);
 
-        before = strv_join(s->before, " ");
-        after = strv_join(s->after, " ");
-        wants = strv_join(s->wants, " ");
-        conflicts = strv_join(s->conflicts, " ");
-
-        if (!before || !after || !wants || !conflicts)
-                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 */
@@ -204,14 +195,14 @@ static int generate_unit_file(SysvStub *s) {
         if (s->description)
                 fprintf(f, "Description=%s\n", s->description);
 
-        if (!isempty(before))
-                fprintf(f, "Before=%s\n", before);
-        if (!isempty(after))
-                fprintf(f, "After=%s\n", after);
-        if (!isempty(wants))
-                fprintf(f, "Wants=%s\n", wants);
-        if (!isempty(conflicts))
-                fprintf(f, "Conflicts=%s\n", conflicts);
+        STRV_FOREACH(p, s->before)
+                fprintf(f, "Before=%s\n", *p);
+        STRV_FOREACH(p, s->after)
+                fprintf(f, "After=%s\n", *p);
+        STRV_FOREACH(p, s->wants)
+                fprintf(f, "Wants=%s\n", *p);
+        STRV_FOREACH(p, s->conflicts)
+                fprintf(f, "Conflicts=%s\n", *p);
 
         fprintf(f,
                 "\n[Service]\n"
index 721e53a..aca5f1e 100644 (file)
@@ -23,6 +23,7 @@ import subprocess
 import tempfile
 import shutil
 from glob import glob
+import collections
 
 try:
     from configparser import RawConfigParser
@@ -32,6 +33,12 @@ except ImportError:
 
 sysv_generator = os.path.join(os.environ.get('builddir', '.'), 'systemd-sysv-generator')
 
+class MultiDict(collections.OrderedDict):
+    def __setitem__(self, key, value):
+        if isinstance(value, list) and key in self:
+            self[key].extend(value)
+        else:
+            super(MultiDict, self).__setitem__(key, value)
 
 class SysvGeneratorTest(unittest.TestCase):
     def setUp(self):
@@ -77,7 +84,14 @@ class SysvGeneratorTest(unittest.TestCase):
         for service in glob(self.out_dir + '/*.service'):
             if os.path.islink(service):
                 continue
-            cp = RawConfigParser()
+            try:
+                # for python3 we need here strict=False to parse multiple
+                # lines with the same key
+                cp = RawConfigParser(dict_type=MultiDict, strict=False)
+            except TypeError:
+                # RawConfigParser in python2 does not have the strict option
+                # but it allows multiple lines with the same key by default
+                cp = RawConfigParser(dict_type=MultiDict)
             cp.optionxform = lambda o: o  # don't lower-case option names
             with open(service) as f:
                 cp.readfp(f)
@@ -224,7 +238,7 @@ class SysvGeneratorTest(unittest.TestCase):
         s = self.run_generator()[1]['foo.service']
         self.assertEqual(set(s.options('Unit')),
                          set(['Documentation', 'SourcePath', 'Description', 'After']))
-        self.assertEqual(s.get('Unit', 'After'), 'nss-lookup.target rpcbind.target')
+        self.assertEqual(s.get('Unit', 'After').split(), ['nss-lookup.target', 'rpcbind.target'])
 
     def test_lsb_deps(self):
         '''LSB header dependencies to other services'''