Repository: ansible/ansible-modules-core

Issue:
Bug: service module: "unable to set rcvar" in Check Mode due to mishandled SystemExit exception
**Issue Type**: Bug report
**Ansible version**: 1.9.4 (but seems to be present in HEAD as well)
**Environment**: FreeBSD 10.1 target

**Summary**:
The service module fails with message `unable to set rcvar` in Check Mode against FreeBSD (and probably any rc.conf-based system) without any error actually occurring

**Steps to reproduce**:

```
$ ansible -C foo -m service -a "name=ntpd state=started enabled=yes"
[...]
foo | FAILED >> {
    "failed": true, 
    "msg": "{\"msg\": \"changing service enablement\", \"changed\": true}\r\n{\"msg\": \"unable to set rcvar\", \"failed\": true}[...]
```

**Cause**:
- service_enable_rcconf() is run inside a try: block
- if changed is True and we are in check_mode, exit_json() is called (L993)
- exit_json() uses sys.exit(), which raises a SystemExit exception
- the calling try: block mishandles the SystemExit exception as the above error

While the issue could be easily fixed like this ...

```
    try:
        return self.service_enable_rcconf()
    except SystemExit:
        pass
    except:
        self.module.fail_json(msg='unable to set rcvar')
```

... I think the larger issue is that every service routine (not just _rcconf()) uses this scheme for an early exit from check_mode and thus would misbehave inside a try: block.

But the above change certainly is an interim solution.



Discussion:

Resolving patch:
diff --git a/system/service.py b/system/service.py
--- a/system/service.py
+++ b/system/service.py
@@ -944,10 +944,11 @@ class FreeBsdService(Service):
 
     def get_service_tools(self):
         self.svc_cmd = self.module.get_bin_path('service', True)
-
         if not self.svc_cmd:
             self.module.fail_json(msg='unable to find service binary')
 
+        self.sysrc_cmd = self.module.get_bin_path('sysrc')
+
     def get_service_status(self):
         rc, stdout, stderr = self.execute_command("%s %s %s %s" % (self.svc_cmd, self.name, 'onestatus', self.arguments))
         if self.name == "pf":
@@ -992,10 +993,32 @@ def service_enable(self):
         if self.rcconf_key is None:
             self.module.fail_json(msg="unable to determine rcvar", stdout=stdout, stderr=stderr)
 
-        try:
-            return self.service_enable_rcconf()
-        except Exception:
-            self.module.fail_json(msg='unable to set rcvar')
+        if self.sysrc_cmd: # FreeBSD >= 9.2
+
+            rc, current_rcconf_value, stderr = self.execute_command("%s -n %s" % (self.sysrc_cmd, self.rcconf_key))
+            if rc != 0:
+                self.module.fail_json(msg="unable to get current rcvar value", stdout=stdout, stderr=stderr)
+
+            if current_rcconf_value.strip().upper() != self.rcconf_value:
+
+                self.changed = True
+
+                if self.module.check_mode:
+                    self.module.exit_json(changed=True, msg="changing service enablement")
+
+                rc, stdout, stderr = self.execute_command("%s %s=\"%s\"" % (self.sysrc_cmd, self.rcconf_key, self.rcconf_value ) )
+                if rc != 0:
+                    self.module.fail_json(msg="unable to set rcvar using sysrc", stdout=stdout, stderr=stderr)
+
+            else:
+                self.changed = False
+
+        else: # Legacy (FreeBSD < 9.2)
+            try:
+                return self.service_enable_rcconf()
+            except Exception:
+                self.module.fail_json(msg='unable to set rcvar')
+
 
     def service_control(self):
 


Test patch: