We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
capabilities: libcap-2.4.3 changed output of getcap
<!--- Verify first that your issue is not already reported on GitHub -->
<!--- Also test if the latest release and devel branch are affected too -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
getcap output changed in version 2.43 vs. 2.26 of libcap, breaking community.general.capabilities module

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
capabilities

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```
ansible 2.10.1
  config file = /home/thomas/git/ansible/ansible.cfg
  configured module search path = ['/home/thomas/git/ansible/library']
  ansible python module location = /home/thomas/.python-venvs/_home_thomas_git_ansible/lib/python3.6/site-packages/ansible
  executable location = /home/thomas/.python-venvs/_home_thomas_git_ansible/bin/ansible
  python version = 3.6.9 (default, Jul 17 2020, 12:50:27) [GCC 8.4.0]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```
CACHE_PLUGIN(/home/thomas/git/ansible/ansible.cfg) = jsonfile
CACHE_PLUGIN_CONNECTION(/home/thomas/git/ansible/ansible.cfg) = factscache
CACHE_PLUGIN_TIMEOUT(/home/thomas/git/ansible/ansible.cfg) = 86400
DEFAULT_BECOME(/home/thomas/git/ansible/ansible.cfg) = True
DEFAULT_BECOME_ASK_PASS(/home/thomas/git/ansible/ansible.cfg) = False
DEFAULT_BECOME_METHOD(/home/thomas/git/ansible/ansible.cfg) = sudo
DEFAULT_BECOME_USER(/home/thomas/git/ansible/ansible.cfg) = root
DEFAULT_CALLBACK_WHITELIST(/home/thomas/git/ansible/ansible.cfg) = ['profile_roles', 'profile_tasks', 'timer']
DEFAULT_FORKS(/home/thomas/git/ansible/ansible.cfg) = 12
DEFAULT_GATHERING(/home/thomas/git/ansible/ansible.cfg) = smart
DEFAULT_HOST_LIST(/home/thomas/git/ansible/ansible.cfg) = ['/home/thomas/git/ansible/inventory/production.py', '/home/thomas/git/ansible/inventory/nameserver_hcloud.yaml']
DEFAULT_MANAGED_STR(/home/thomas/git/ansible/ansible.cfg) = This file is managed by Ansible. Do not edit manually!
DEFAULT_MODULE_PATH(/home/thomas/git/ansible/ansible.cfg) = ['/home/thomas/git/ansible/library']
INTERPRETER_PYTHON(/home/thomas/git/ansible/ansible.cfg) = auto_silent
INVENTORY_ENABLED(/home/thomas/git/ansible/ansible.cfg) = ['script', 'hcloud']
MAX_FILE_SIZE_FOR_DIFF(/home/thomas/git/ansible/ansible.cfg) = 1048576
```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->
Host running ansible is Ubuntu 18.04, target system is Gentoo with sys-libs/libcap-2.4.3

##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
- name: ensure capabilities for ping are set
  community.general.capabilities:
    path: /bin/ping
    capability: cap_net_raw+ep
    state: present
```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
Ansible sets cap_net_raw+ep if it is not already set

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
Ansible fails getting current capabilities.

<!--- Paste verbatim command output between quotes -->
```paste below
fatal: [server20.tralios.de]: FAILED! => {
    "changed": false,
    "invocation": {
        "module_args": {
            "capability": "cap_net_raw+ep",
            "path": "/bin/ping",
            "state": "present"
        }
    },
    "msg": "Unable to get capabilities of /bin/ping",
    "stderr": "",
    "stderr_lines": [],
    "stdout": "/bin/ping cap_net_raw=ep",
    "stdout_lines": [
        "/bin/ping cap_net_raw=ep"
    ]
}
```

##### Additional information

Version 2.26:
```
➜  touch capabilities_test
➜  sudo setcap cap_net_raw+ep capabilities_test
➜  getcap capabilities_test 
capabilities_test = cap_net_raw+ep
```

Version 2.43:
```
➜  getcap capabilities_test 
capabilities_test cap_net_raw=ep
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `plugins/modules/system/capabilities.py`
Content:
```
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 
4 # Copyright: (c) 2014, Nate Coraor <nate@bx.psu.edu>
5 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 
7 from __future__ import absolute_import, division, print_function
8 __metaclass__ = type
9 
10 DOCUMENTATION = r'''
11 ---
12 module: capabilities
13 short_description: Manage Linux capabilities
14 description:
15     - This module manipulates files privileges using the Linux capabilities(7) system.
16 options:
17     path:
18         description:
19             - Specifies the path to the file to be managed.
20         type: str
21         required: yes
22         aliases: [ key ]
23     capability:
24         description:
25             - Desired capability to set (with operator and flags, if state is C(present)) or remove (if state is C(absent))
26         type: str
27         required: yes
28         aliases: [ cap ]
29     state:
30         description:
31             - Whether the entry should be present or absent in the file's capabilities.
32         type: str
33         choices: [ absent, present ]
34         default: present
35 notes:
36     - The capabilities system will automatically transform operators and flags into the effective set,
37       so for example, C(cap_foo=ep) will probably become C(cap_foo+ep).
38     - This module does not attempt to determine the final operator and flags to compare,
39       so you will want to ensure that your capabilities argument matches the final capabilities.
40 author:
41 - Nate Coraor (@natefoo)
42 '''
43 
44 EXAMPLES = r'''
45 - name: Set cap_sys_chroot+ep on /foo
46   community.general.capabilities:
47     path: /foo
48     capability: cap_sys_chroot+ep
49     state: present
50 
51 - name: Remove cap_net_bind_service from /bar
52   community.general.capabilities:
53     path: /bar
54     capability: cap_net_bind_service
55     state: absent
56 '''
57 
58 from ansible.module_utils.basic import AnsibleModule
59 
60 OPS = ('=', '-', '+')
61 
62 
63 class CapabilitiesModule(object):
64     platform = 'Linux'
65     distribution = None
66 
67     def __init__(self, module):
68         self.module = module
69         self.path = module.params['path'].strip()
70         self.capability = module.params['capability'].strip().lower()
71         self.state = module.params['state']
72         self.getcap_cmd = module.get_bin_path('getcap', required=True)
73         self.setcap_cmd = module.get_bin_path('setcap', required=True)
74         self.capability_tup = self._parse_cap(self.capability, op_required=self.state == 'present')
75 
76         self.run()
77 
78     def run(self):
79 
80         current = self.getcap(self.path)
81         caps = [cap[0] for cap in current]
82 
83         if self.state == 'present' and self.capability_tup not in current:
84             # need to add capability
85             if self.module.check_mode:
86                 self.module.exit_json(changed=True, msg='capabilities changed')
87             else:
88                 # remove from current cap list if it's already set (but op/flags differ)
89                 current = list(filter(lambda x: x[0] != self.capability_tup[0], current))
90                 # add new cap with correct op/flags
91                 current.append(self.capability_tup)
92                 self.module.exit_json(changed=True, state=self.state, msg='capabilities changed', stdout=self.setcap(self.path, current))
93         elif self.state == 'absent' and self.capability_tup[0] in caps:
94             # need to remove capability
95             if self.module.check_mode:
96                 self.module.exit_json(changed=True, msg='capabilities changed')
97             else:
98                 # remove from current cap list and then set current list
99                 current = filter(lambda x: x[0] != self.capability_tup[0], current)
100                 self.module.exit_json(changed=True, state=self.state, msg='capabilities changed', stdout=self.setcap(self.path, current))
101         self.module.exit_json(changed=False, state=self.state)
102 
103     def getcap(self, path):
104         rval = []
105         cmd = "%s -v %s" % (self.getcap_cmd, path)
106         rc, stdout, stderr = self.module.run_command(cmd)
107         # If file xattrs are set but no caps are set the output will be:
108         #   '/foo ='
109         # If file xattrs are unset the output will be:
110         #   '/foo'
111         # If the file does not exist the output will be (with rc == 0...):
112         #   '/foo (No such file or directory)'
113         if rc != 0 or (stdout.strip() != path and stdout.count(' =') != 1):
114             self.module.fail_json(msg="Unable to get capabilities of %s" % path, stdout=stdout.strip(), stderr=stderr)
115         if stdout.strip() != path:
116             caps = stdout.split(' =')[1].strip().split()
117             for cap in caps:
118                 cap = cap.lower()
119                 # getcap condenses capabilities with the same op/flags into a
120                 # comma-separated list, so we have to parse that
121                 if ',' in cap:
122                     cap_group = cap.split(',')
123                     cap_group[-1], op, flags = self._parse_cap(cap_group[-1])
124                     for subcap in cap_group:
125                         rval.append((subcap, op, flags))
126                 else:
127                     rval.append(self._parse_cap(cap))
128         return rval
129 
130     def setcap(self, path, caps):
131         caps = ' '.join([''.join(cap) for cap in caps])
132         cmd = "%s '%s' %s" % (self.setcap_cmd, caps, path)
133         rc, stdout, stderr = self.module.run_command(cmd)
134         if rc != 0:
135             self.module.fail_json(msg="Unable to set capabilities of %s" % path, stdout=stdout, stderr=stderr)
136         else:
137             return stdout
138 
139     def _parse_cap(self, cap, op_required=True):
140         opind = -1
141         try:
142             i = 0
143             while opind == -1:
144                 opind = cap.find(OPS[i])
145                 i += 1
146         except Exception:
147             if op_required:
148                 self.module.fail_json(msg="Couldn't find operator (one of: %s)" % str(OPS))
149             else:
150                 return (cap, None, None)
151         op = cap[opind]
152         cap, flags = cap.split(op)
153         return (cap, op, flags)
154 
155 
156 # ==============================================================
157 # main
158 
159 def main():
160     # defining module
161     module = AnsibleModule(
162         argument_spec=dict(
163             path=dict(type='str', required=True, aliases=['key']),
164             capability=dict(type='str', required=True, aliases=['cap']),
165             state=dict(type='str', default='present', choices=['absent', 'present']),
166         ),
167         supports_check_mode=True,
168     )
169 
170     CapabilitiesModule(module)
171 
172 
173 if __name__ == '__main__':
174     main()
175
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/plugins/modules/system/capabilities.py b/plugins/modules/system/capabilities.py
--- a/plugins/modules/system/capabilities.py
+++ b/plugins/modules/system/capabilities.py
@@ -108,12 +108,18 @@
         #   '/foo ='
         # If file xattrs are unset the output will be:
         #   '/foo'
-        # If the file does not exist the output will be (with rc == 0...):
+        # If the file does not exist, the stderr will be (with rc == 0...):
         #   '/foo (No such file or directory)'
-        if rc != 0 or (stdout.strip() != path and stdout.count(' =') != 1):
+        if rc != 0 or stderr != "":
             self.module.fail_json(msg="Unable to get capabilities of %s" % path, stdout=stdout.strip(), stderr=stderr)
         if stdout.strip() != path:
-            caps = stdout.split(' =')[1].strip().split()
+            if ' =' in stdout:
+                # process output of an older version of libcap
+                caps = stdout.split(' =')[1].strip().split()
+            else:
+                # otherwise, we have a newer version here
+                # see original commit message of cap/v0.2.40-18-g177cd41 in libcap.git
+                caps = stdout.split()[1].strip().split()
             for cap in caps:
                 cap = cap.lower()
                 # getcap condenses capabilities with the same op/flags into a

{"golden_diff": "diff --git a/plugins/modules/system/capabilities.py b/plugins/modules/system/capabilities.py\n--- a/plugins/modules/system/capabilities.py\n+++ b/plugins/modules/system/capabilities.py\n@@ -108,12 +108,18 @@\n         #   '/foo ='\n         # If file xattrs are unset the output will be:\n         #   '/foo'\n-        # If the file does not exist the output will be (with rc == 0...):\n+        # If the file does not exist, the stderr will be (with rc == 0...):\n         #   '/foo (No such file or directory)'\n-        if rc != 0 or (stdout.strip() != path and stdout.count(' =') != 1):\n+        if rc != 0 or stderr != \"\":\n             self.module.fail_json(msg=\"Unable to get capabilities of %s\" % path, stdout=stdout.strip(), stderr=stderr)\n         if stdout.strip() != path:\n-            caps = stdout.split(' =')[1].strip().split()\n+            if ' =' in stdout:\n+                # process output of an older version of libcap\n+                caps = stdout.split(' =')[1].strip().split()\n+            else:\n+                # otherwise, we have a newer version here\n+                # see original commit message of cap/v0.2.40-18-g177cd41 in libcap.git\n+                caps = stdout.split()[1].strip().split()\n             for cap in caps:\n                 cap = cap.lower()\n                 # getcap condenses capabilities with the same op/flags into a\n", "issue": "capabilities: libcap-2.4.3 changed output of getcap\n<!--- Verify first that your issue is not already reported on GitHub -->\r\n<!--- Also test if the latest release and devel branch are affected too -->\r\n<!--- Complete *all* sections as described, this form is processed automatically -->\r\n\r\n##### SUMMARY\r\ngetcap output changed in version 2.43 vs. 2.26 of libcap, breaking community.general.capabilities module\r\n\r\n##### ISSUE TYPE\r\n- Bug Report\r\n\r\n##### COMPONENT NAME\r\ncapabilities\r\n\r\n##### ANSIBLE VERSION\r\n<!--- Paste verbatim output from \"ansible --version\" between quotes -->\r\n```\r\nansible 2.10.1\r\n  config file = /home/thomas/git/ansible/ansible.cfg\r\n  configured module search path = ['/home/thomas/git/ansible/library']\r\n  ansible python module location = /home/thomas/.python-venvs/_home_thomas_git_ansible/lib/python3.6/site-packages/ansible\r\n  executable location = /home/thomas/.python-venvs/_home_thomas_git_ansible/bin/ansible\r\n  python version = 3.6.9 (default, Jul 17 2020, 12:50:27) [GCC 8.4.0]\r\n```\r\n\r\n##### CONFIGURATION\r\n<!--- Paste verbatim output from \"ansible-config dump --only-changed\" between quotes -->\r\n```\r\nCACHE_PLUGIN(/home/thomas/git/ansible/ansible.cfg) = jsonfile\r\nCACHE_PLUGIN_CONNECTION(/home/thomas/git/ansible/ansible.cfg) = factscache\r\nCACHE_PLUGIN_TIMEOUT(/home/thomas/git/ansible/ansible.cfg) = 86400\r\nDEFAULT_BECOME(/home/thomas/git/ansible/ansible.cfg) = True\r\nDEFAULT_BECOME_ASK_PASS(/home/thomas/git/ansible/ansible.cfg) = False\r\nDEFAULT_BECOME_METHOD(/home/thomas/git/ansible/ansible.cfg) = sudo\r\nDEFAULT_BECOME_USER(/home/thomas/git/ansible/ansible.cfg) = root\r\nDEFAULT_CALLBACK_WHITELIST(/home/thomas/git/ansible/ansible.cfg) = ['profile_roles', 'profile_tasks', 'timer']\r\nDEFAULT_FORKS(/home/thomas/git/ansible/ansible.cfg) = 12\r\nDEFAULT_GATHERING(/home/thomas/git/ansible/ansible.cfg) = smart\r\nDEFAULT_HOST_LIST(/home/thomas/git/ansible/ansible.cfg) = ['/home/thomas/git/ansible/inventory/production.py', '/home/thomas/git/ansible/inventory/nameserver_hcloud.yaml']\r\nDEFAULT_MANAGED_STR(/home/thomas/git/ansible/ansible.cfg) = This file is managed by Ansible. Do not edit manually!\r\nDEFAULT_MODULE_PATH(/home/thomas/git/ansible/ansible.cfg) = ['/home/thomas/git/ansible/library']\r\nINTERPRETER_PYTHON(/home/thomas/git/ansible/ansible.cfg) = auto_silent\r\nINVENTORY_ENABLED(/home/thomas/git/ansible/ansible.cfg) = ['script', 'hcloud']\r\nMAX_FILE_SIZE_FOR_DIFF(/home/thomas/git/ansible/ansible.cfg) = 1048576\r\n```\r\n\r\n##### OS / ENVIRONMENT\r\n<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->\r\nHost running ansible is Ubuntu 18.04, target system is Gentoo with sys-libs/libcap-2.4.3\r\n\r\n##### STEPS TO REPRODUCE\r\n<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->\r\n\r\n<!--- Paste example playbooks or commands between quotes below -->\r\n```yaml\r\n- name: ensure capabilities for ping are set\r\n  community.general.capabilities:\r\n    path: /bin/ping\r\n    capability: cap_net_raw+ep\r\n    state: present\r\n```\r\n\r\n<!--- HINT: You can paste gist.github.com links for larger files -->\r\n\r\n##### EXPECTED RESULTS\r\n<!--- Describe what you expected to happen when running the steps above -->\r\nAnsible sets cap_net_raw+ep if it is not already set\r\n\r\n##### ACTUAL RESULTS\r\n<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->\r\nAnsible fails getting current capabilities.\r\n\r\n<!--- Paste verbatim command output between quotes -->\r\n```paste below\r\nfatal: [server20.tralios.de]: FAILED! => {\r\n    \"changed\": false,\r\n    \"invocation\": {\r\n        \"module_args\": {\r\n            \"capability\": \"cap_net_raw+ep\",\r\n            \"path\": \"/bin/ping\",\r\n            \"state\": \"present\"\r\n        }\r\n    },\r\n    \"msg\": \"Unable to get capabilities of /bin/ping\",\r\n    \"stderr\": \"\",\r\n    \"stderr_lines\": [],\r\n    \"stdout\": \"/bin/ping cap_net_raw=ep\",\r\n    \"stdout_lines\": [\r\n        \"/bin/ping cap_net_raw=ep\"\r\n    ]\r\n}\r\n```\r\n\r\n##### Additional information\r\n\r\nVersion 2.26:\r\n```\r\n\u279c  touch capabilities_test\r\n\u279c  sudo setcap cap_net_raw+ep capabilities_test\r\n\u279c  getcap capabilities_test \r\ncapabilities_test = cap_net_raw+ep\r\n```\r\n\r\nVersion 2.43:\r\n```\r\n\u279c  getcap capabilities_test \r\ncapabilities_test cap_net_raw=ep\r\n```\n", "before_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n# Copyright: (c) 2014, Nate Coraor <nate@bx.psu.edu>\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\nDOCUMENTATION = r'''\n---\nmodule: capabilities\nshort_description: Manage Linux capabilities\ndescription:\n    - This module manipulates files privileges using the Linux capabilities(7) system.\noptions:\n    path:\n        description:\n            - Specifies the path to the file to be managed.\n        type: str\n        required: yes\n        aliases: [ key ]\n    capability:\n        description:\n            - Desired capability to set (with operator and flags, if state is C(present)) or remove (if state is C(absent))\n        type: str\n        required: yes\n        aliases: [ cap ]\n    state:\n        description:\n            - Whether the entry should be present or absent in the file's capabilities.\n        type: str\n        choices: [ absent, present ]\n        default: present\nnotes:\n    - The capabilities system will automatically transform operators and flags into the effective set,\n      so for example, C(cap_foo=ep) will probably become C(cap_foo+ep).\n    - This module does not attempt to determine the final operator and flags to compare,\n      so you will want to ensure that your capabilities argument matches the final capabilities.\nauthor:\n- Nate Coraor (@natefoo)\n'''\n\nEXAMPLES = r'''\n- name: Set cap_sys_chroot+ep on /foo\n  community.general.capabilities:\n    path: /foo\n    capability: cap_sys_chroot+ep\n    state: present\n\n- name: Remove cap_net_bind_service from /bar\n  community.general.capabilities:\n    path: /bar\n    capability: cap_net_bind_service\n    state: absent\n'''\n\nfrom ansible.module_utils.basic import AnsibleModule\n\nOPS = ('=', '-', '+')\n\n\nclass CapabilitiesModule(object):\n    platform = 'Linux'\n    distribution = None\n\n    def __init__(self, module):\n        self.module = module\n        self.path = module.params['path'].strip()\n        self.capability = module.params['capability'].strip().lower()\n        self.state = module.params['state']\n        self.getcap_cmd = module.get_bin_path('getcap', required=True)\n        self.setcap_cmd = module.get_bin_path('setcap', required=True)\n        self.capability_tup = self._parse_cap(self.capability, op_required=self.state == 'present')\n\n        self.run()\n\n    def run(self):\n\n        current = self.getcap(self.path)\n        caps = [cap[0] for cap in current]\n\n        if self.state == 'present' and self.capability_tup not in current:\n            # need to add capability\n            if self.module.check_mode:\n                self.module.exit_json(changed=True, msg='capabilities changed')\n            else:\n                # remove from current cap list if it's already set (but op/flags differ)\n                current = list(filter(lambda x: x[0] != self.capability_tup[0], current))\n                # add new cap with correct op/flags\n                current.append(self.capability_tup)\n                self.module.exit_json(changed=True, state=self.state, msg='capabilities changed', stdout=self.setcap(self.path, current))\n        elif self.state == 'absent' and self.capability_tup[0] in caps:\n            # need to remove capability\n            if self.module.check_mode:\n                self.module.exit_json(changed=True, msg='capabilities changed')\n            else:\n                # remove from current cap list and then set current list\n                current = filter(lambda x: x[0] != self.capability_tup[0], current)\n                self.module.exit_json(changed=True, state=self.state, msg='capabilities changed', stdout=self.setcap(self.path, current))\n        self.module.exit_json(changed=False, state=self.state)\n\n    def getcap(self, path):\n        rval = []\n        cmd = \"%s -v %s\" % (self.getcap_cmd, path)\n        rc, stdout, stderr = self.module.run_command(cmd)\n        # If file xattrs are set but no caps are set the output will be:\n        #   '/foo ='\n        # If file xattrs are unset the output will be:\n        #   '/foo'\n        # If the file does not exist the output will be (with rc == 0...):\n        #   '/foo (No such file or directory)'\n        if rc != 0 or (stdout.strip() != path and stdout.count(' =') != 1):\n            self.module.fail_json(msg=\"Unable to get capabilities of %s\" % path, stdout=stdout.strip(), stderr=stderr)\n        if stdout.strip() != path:\n            caps = stdout.split(' =')[1].strip().split()\n            for cap in caps:\n                cap = cap.lower()\n                # getcap condenses capabilities with the same op/flags into a\n                # comma-separated list, so we have to parse that\n                if ',' in cap:\n                    cap_group = cap.split(',')\n                    cap_group[-1], op, flags = self._parse_cap(cap_group[-1])\n                    for subcap in cap_group:\n                        rval.append((subcap, op, flags))\n                else:\n                    rval.append(self._parse_cap(cap))\n        return rval\n\n    def setcap(self, path, caps):\n        caps = ' '.join([''.join(cap) for cap in caps])\n        cmd = \"%s '%s' %s\" % (self.setcap_cmd, caps, path)\n        rc, stdout, stderr = self.module.run_command(cmd)\n        if rc != 0:\n            self.module.fail_json(msg=\"Unable to set capabilities of %s\" % path, stdout=stdout, stderr=stderr)\n        else:\n            return stdout\n\n    def _parse_cap(self, cap, op_required=True):\n        opind = -1\n        try:\n            i = 0\n            while opind == -1:\n                opind = cap.find(OPS[i])\n                i += 1\n        except Exception:\n            if op_required:\n                self.module.fail_json(msg=\"Couldn't find operator (one of: %s)\" % str(OPS))\n            else:\n                return (cap, None, None)\n        op = cap[opind]\n        cap, flags = cap.split(op)\n        return (cap, op, flags)\n\n\n# ==============================================================\n# main\n\ndef main():\n    # defining module\n    module = AnsibleModule(\n        argument_spec=dict(\n            path=dict(type='str', required=True, aliases=['key']),\n            capability=dict(type='str', required=True, aliases=['cap']),\n            state=dict(type='str', default='present', choices=['absent', 'present']),\n        ),\n        supports_check_mode=True,\n    )\n\n    CapabilitiesModule(module)\n\n\nif __name__ == '__main__':\n    main()\n", "path": "plugins/modules/system/capabilities.py"}], "after_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n# Copyright: (c) 2014, Nate Coraor <nate@bx.psu.edu>\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\nDOCUMENTATION = r'''\n---\nmodule: capabilities\nshort_description: Manage Linux capabilities\ndescription:\n    - This module manipulates files privileges using the Linux capabilities(7) system.\noptions:\n    path:\n        description:\n            - Specifies the path to the file to be managed.\n        type: str\n        required: yes\n        aliases: [ key ]\n    capability:\n        description:\n            - Desired capability to set (with operator and flags, if state is C(present)) or remove (if state is C(absent))\n        type: str\n        required: yes\n        aliases: [ cap ]\n    state:\n        description:\n            - Whether the entry should be present or absent in the file's capabilities.\n        type: str\n        choices: [ absent, present ]\n        default: present\nnotes:\n    - The capabilities system will automatically transform operators and flags into the effective set,\n      so for example, C(cap_foo=ep) will probably become C(cap_foo+ep).\n    - This module does not attempt to determine the final operator and flags to compare,\n      so you will want to ensure that your capabilities argument matches the final capabilities.\nauthor:\n- Nate Coraor (@natefoo)\n'''\n\nEXAMPLES = r'''\n- name: Set cap_sys_chroot+ep on /foo\n  community.general.capabilities:\n    path: /foo\n    capability: cap_sys_chroot+ep\n    state: present\n\n- name: Remove cap_net_bind_service from /bar\n  community.general.capabilities:\n    path: /bar\n    capability: cap_net_bind_service\n    state: absent\n'''\n\nfrom ansible.module_utils.basic import AnsibleModule\n\nOPS = ('=', '-', '+')\n\n\nclass CapabilitiesModule(object):\n    platform = 'Linux'\n    distribution = None\n\n    def __init__(self, module):\n        self.module = module\n        self.path = module.params['path'].strip()\n        self.capability = module.params['capability'].strip().lower()\n        self.state = module.params['state']\n        self.getcap_cmd = module.get_bin_path('getcap', required=True)\n        self.setcap_cmd = module.get_bin_path('setcap', required=True)\n        self.capability_tup = self._parse_cap(self.capability, op_required=self.state == 'present')\n\n        self.run()\n\n    def run(self):\n\n        current = self.getcap(self.path)\n        caps = [cap[0] for cap in current]\n\n        if self.state == 'present' and self.capability_tup not in current:\n            # need to add capability\n            if self.module.check_mode:\n                self.module.exit_json(changed=True, msg='capabilities changed')\n            else:\n                # remove from current cap list if it's already set (but op/flags differ)\n                current = list(filter(lambda x: x[0] != self.capability_tup[0], current))\n                # add new cap with correct op/flags\n                current.append(self.capability_tup)\n                self.module.exit_json(changed=True, state=self.state, msg='capabilities changed', stdout=self.setcap(self.path, current))\n        elif self.state == 'absent' and self.capability_tup[0] in caps:\n            # need to remove capability\n            if self.module.check_mode:\n                self.module.exit_json(changed=True, msg='capabilities changed')\n            else:\n                # remove from current cap list and then set current list\n                current = filter(lambda x: x[0] != self.capability_tup[0], current)\n                self.module.exit_json(changed=True, state=self.state, msg='capabilities changed', stdout=self.setcap(self.path, current))\n        self.module.exit_json(changed=False, state=self.state)\n\n    def getcap(self, path):\n        rval = []\n        cmd = \"%s -v %s\" % (self.getcap_cmd, path)\n        rc, stdout, stderr = self.module.run_command(cmd)\n        # If file xattrs are set but no caps are set the output will be:\n        #   '/foo ='\n        # If file xattrs are unset the output will be:\n        #   '/foo'\n        # If the file does not exist, the stderr will be (with rc == 0...):\n        #   '/foo (No such file or directory)'\n        if rc != 0 or stderr != \"\":\n            self.module.fail_json(msg=\"Unable to get capabilities of %s\" % path, stdout=stdout.strip(), stderr=stderr)\n        if stdout.strip() != path:\n            if ' =' in stdout:\n                # process output of an older version of libcap\n                caps = stdout.split(' =')[1].strip().split()\n            else:\n                # otherwise, we have a newer version here\n                # see original commit message of cap/v0.2.40-18-g177cd41 in libcap.git\n                caps = stdout.split()[1].strip().split()\n            for cap in caps:\n                cap = cap.lower()\n                # getcap condenses capabilities with the same op/flags into a\n                # comma-separated list, so we have to parse that\n                if ',' in cap:\n                    cap_group = cap.split(',')\n                    cap_group[-1], op, flags = self._parse_cap(cap_group[-1])\n                    for subcap in cap_group:\n                        rval.append((subcap, op, flags))\n                else:\n                    rval.append(self._parse_cap(cap))\n        return rval\n\n    def setcap(self, path, caps):\n        caps = ' '.join([''.join(cap) for cap in caps])\n        cmd = \"%s '%s' %s\" % (self.setcap_cmd, caps, path)\n        rc, stdout, stderr = self.module.run_command(cmd)\n        if rc != 0:\n            self.module.fail_json(msg=\"Unable to set capabilities of %s\" % path, stdout=stdout, stderr=stderr)\n        else:\n            return stdout\n\n    def _parse_cap(self, cap, op_required=True):\n        opind = -1\n        try:\n            i = 0\n            while opind == -1:\n                opind = cap.find(OPS[i])\n                i += 1\n        except Exception:\n            if op_required:\n                self.module.fail_json(msg=\"Couldn't find operator (one of: %s)\" % str(OPS))\n            else:\n                return (cap, None, None)\n        op = cap[opind]\n        cap, flags = cap.split(op)\n        return (cap, op, flags)\n\n\n# ==============================================================\n# main\n\ndef main():\n    # defining module\n    module = AnsibleModule(\n        argument_spec=dict(\n            path=dict(type='str', required=True, aliases=['key']),\n            capability=dict(type='str', required=True, aliases=['cap']),\n            state=dict(type='str', default='present', choices=['absent', 'present']),\n        ),\n        supports_check_mode=True,\n    )\n\n    CapabilitiesModule(module)\n\n\nif __name__ == '__main__':\n    main()\n", "path": "plugins/modules/system/capabilities.py"}]}