Blob Blame History Raw
Adapted version of
 
commit b3b451d6f8946986b8f50c8bcddeef50ed7a5f8f
Author: Jiri Popelka <jpopelka@redhat.com>
Date:   Mon Oct 27 17:45:46 2014 +0100

    ipXtables: use -w or -w2 if supported
    
    iptables (since 1.4.20) has a locking mechanism [1],
    which prevents multiple instances of the program from running concurrently.
    
    It can happen that some other daemon (libvirtd [2], docker [3])
    is calling iptables at the same time as firewalld.
    In that case the second one which calls iptables fails with:
    "Another app is currently holding the xtables lock.
    Perhaps you want to use the -w option?"
    
    The easiest work-around is the use the suggested "-w" option,
    which makes the second iptables instance wait till the lock is released.
    Even better is to use "-w2" [2] which makes it wait for max 2 seconds.
    
    [1] https://git.netfilter.org/iptables/commit/?id=93587a04d0f2511e108bbc4d87a8b9d28a5c5dd8
    [2] https://bugzilla.redhat.com/show_bug.cgi?id=1098281
    [3] https://bugzilla.redhat.com/show_bug.cgi?id=1151067
    [4] https://git.netfilter.org/iptables/commit/?id=aaa4ace72ba1d195bbf436134a336816c33f7bd0

diff --git a/src/firewall/core/ipXtables.py b/src/firewall/core/ipXtables.py
index 8f61bb8..e8381f2 100644
--- a/src/firewall/core/ipXtables.py
+++ b/src/firewall/core/ipXtables.py
@@ -125,10 +125,14 @@ class ip4tables:
 
     def __init__(self):
         self._command = COMMAND[self.ipv]
+        self.wait_option = self._detect_wait_option()
 
     def __run(self, args):
         # convert to string list
-        _args = ["%s" % item for item in args]
+        if self.wait_option:
+            _args = [self.wait_option] + ["%s" % item for item in args]
+        else:
+            _args = ["%s" % item for item in args]
         log.debug2("%s: %s %s", self.__class__, self._command, " ".join(_args))
         (status, ret) = runProg(self._command, _args)
         if status != 0:
@@ -170,6 +174,18 @@ class ip4tables:
 
         return tables
 
+    def _detect_wait_option(self):
+        wait_option = ""
+        (status, ret) = runProg(self._command, ["-w", "-L"])  # since iptables-1.4.20
+        if status == 0:
+            wait_option = "-w"  # wait for xtables lock
+            (status, ret) = runProg(self._command, ["-w2", "-L"])  # since iptables > 1.4.21
+            if status == 0:
+                wait_option = "-w2"  # wait max 2 seconds
+            log.debug2("%s: %s will be using %s option.", self.__class__, self._command, wait_option)
+
+        return wait_option
+
     def flush(self):
         tables = self.used_tables()
         for table in tables: