Repository: ansible-collections/community.general

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
```


Discussion:
Files identified in the description:
None

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
Files identified in the description:
* [`plugins/modules/system/capabilities.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/system/capabilities.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @MorrisA @bcoca @d-little @flynn1973 @gforster @kairoaraujo @marvin-sinister @mator @molekuul @natefoo @ramooncamacho @wtcross
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
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 @@ def getcap(self, path):
         #   '/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


Test patch: