@ -54,6 +54,12 @@ options:
- Whether the service should start on boot. At least one of state and
- Whether the service should start on boot. At least one of state and
enabled are required.
enabled are required.
runlevel:
required: false
description:
- The runlevel that this service belongs to. Needed by OpenRC system
(e.g. Gentoo), and default to "default" if not set.
arguments:
arguments:
description:
description:
- Additional arguments provided on the command line
- Additional arguments provided on the command line
@ -85,6 +91,7 @@ EXAMPLES = '''
import platform
import platform
import os
import os
import re
import tempfile
import tempfile
import shlex
import shlex
import select
import select
@ -115,8 +122,10 @@ class Service(object):
self.state = module.params['state']
self.state = module.params['state']
self.pattern = module.params['pattern']
self.pattern = module.params['pattern']
self.enable = module.params['enabled']
self.enable = module.params['enabled']
self.runlevel = module.params['runlevel']
self.changed = False
self.changed = False
self.running = None
self.running = None
self.crashed = None
self.action = None
self.action = None
self.svc_cmd = None
self.svc_cmd = None
self.svc_initscript = None
self.svc_initscript = None
@ -360,7 +369,7 @@ class LinuxService(Service):
def get_service_tools(self):
def get_service_tools(self):
paths = [ '/sbin', '/usr/sbin', '/bin', '/usr/bin' ]
paths = [ '/sbin', '/usr/sbin', '/bin', '/usr/bin' ]
binaries = [ 'service', 'chkconfig', 'update-rc.d', 'initctl', 'systemctl', 'start', 'stop', 'restart' ]
binaries = [ 'service', 'chkconfig', 'update-rc.d', 'rc-service', 'rc-update', ' initctl', 'systemctl', 'start', 'stop', 'restart' ]
initpaths = [ '/etc/init.d' ]
initpaths = [ '/etc/init.d' ]
location = dict()
location = dict()
@ -379,6 +388,11 @@ class LinuxService(Service):
elif location.get('update-rc.d', None) and os.path.exists("/etc/init.d/%s" % self.name):
elif location.get('update-rc.d', None) and os.path.exists("/etc/init.d/%s" % self.name):
# service is managed by with SysV init scripts, but with update-rc.d
# service is managed by with SysV init scripts, but with update-rc.d
self.enable_cmd = location['update-rc.d']
self.enable_cmd = location['update-rc.d']
elif location.get('rc-service', None) and not location.get('systemctl', None):
# service is managed by OpenRC
self.svc_cmd = location['rc-service']
self.enable_cmd = location['rc-update']
return
elif location.get('systemctl', None):
elif location.get('systemctl', None):
# verify service is managed by systemd
# verify service is managed by systemd
@ -435,6 +449,11 @@ class LinuxService(Service):
elif initctl_status_stdout.find("start/running") != -1:
elif initctl_status_stdout.find("start/running") != -1:
self.running = True
self.running = True
if self.svc_cmd.endswith("rc-service") and self.running is None:
openrc_rc, openrc_status_stdout, openrc_status_stderr = self.execute_command("%s %s status" % (self.svc_cmd, self.name))
self.running = "started" in openrc_status_stdout
self.crashed = "crashed" in openrc_status_stderr
# if the job status is still not known check it by response code
# if the job status is still not known check it by response code
# For reference, see:
# For reference, see:
# http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
# http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
@ -509,19 +528,44 @@ class LinuxService(Service):
elif not self.enable:
elif not self.enable:
return
return
# we change argument depending on real binary used
if self.enable_cmd.endswith("rc-update"):
# update-rc.d wants enable/disable while
(rc, out, err) = self.execute_command("%s show" % self.enable_cmd)
# chkconfig wants on/off
for line in out.splitlines():
# also, systemctl needs the argument order reversed
service_name, runlevels = line.split('|')
service_name = service_name.strip()
if service_name != self.name:
continue
runlevels = re.split(r'\s+', runlevels)
# service already enabled for the runlevel
if self.enable and self.runlevel in runlevels:
return
# service already disabled for the runlevel
elif not self.enable and self.runlevel not in runlevels:
return
break
else:
# service already disabled altogether
if not self.enable:
return
# we change argument depending on real binary used:
# - update-rc.d and systemctl wants enable/disable
# - chkconfig wants on/off
# - rc-update wants add/delete
# also, rc-update and systemctl needs the argument order reversed
if self.enable:
if self.enable:
on_off = "on"
on_off = "on"
enable_disable = "enable"
enable_disable = "enable"
add_delete = "add"
else:
else:
on_off = "off"
on_off = "off"
enable_disable = "disable"
enable_disable = "disable"
add_delete = "delete"
if self.enable_cmd.endswith("update-rc.d"):
if self.enable_cmd.endswith("update-rc.d"):
args = (self.enable_cmd, self.name, enable_disable)
args = (self.enable_cmd, self.name, enable_disable)
elif self.enable_cmd.endswith("rc-update"):
args = (self.enable_cmd, add_delete, self.name + " " + self.runlevel)
elif self.enable_cmd.endswith("systemctl"):
elif self.enable_cmd.endswith("systemctl"):
args = (self.enable_cmd, enable_disable, self.name + ".service")
args = (self.enable_cmd, enable_disable, self.name + ".service")
else:
else:
@ -542,7 +586,7 @@ class LinuxService(Service):
arguments = self.arguments
arguments = self.arguments
if self.svc_cmd:
if self.svc_cmd:
if not self.svc_cmd.endswith("systemctl"):
if not self.svc_cmd.endswith("systemctl"):
# SysV take the form <cmd> <name> <action>
# SysV and OpenRC take the form <cmd> <name> <action>
svc_cmd = "%s %s" % (self.svc_cmd, self.name)
svc_cmd = "%s %s" % (self.svc_cmd, self.name)
else:
else:
# systemd commands take the form <cmd> <action> <name>
# systemd commands take the form <cmd> <action> <name>
@ -552,15 +596,23 @@ class LinuxService(Service):
# upstart
# upstart
svc_cmd = "%s" % self.svc_initscript
svc_cmd = "%s" % self.svc_initscript
# In OpenRC, if a service crashed, we need to reset its status to
# stopped with the zap command, before we can start it back.
if self.svc_cmd.endswith('rc-service') and self.action == 'start' and self.crashed:
self.execute_command("%s zap" % svc_cmd, daemonize=True)
if self.action is not "restart":
if self.action is not "restart":
if svc_cmd != '':
if svc_cmd != '':
# upstart or systemd
# upstart or systemd or OpenRC
rc_state, stdout, stderr = self.execute_command("%s %s %s" % (svc_cmd, self.action, arguments), daemonize=True)
rc_state, stdout, stderr = self.execute_command("%s %s %s" % (svc_cmd, self.action, arguments), daemonize=True)
else:
else:
# SysV
# SysV
rc_state, stdout, stderr = self.execute_command("%s %s %s" % (self.action, self.name, arguments), daemonize=True)
rc_state, stdout, stderr = self.execute_command("%s %s %s" % (self.action, self.name, arguments), daemonize=True)
elif self.svc_cmd.endswith('rc-service'):
# All services in OpenRC support restart.
rc_state, stdout, stderr = self.execute_command("%s %s %s" % (svc_cmd, self.action, arguments), daemonize=True)
else:
else:
# not all services support restart. Do it the hard way.
# In other systems, not all services support restart. Do it the hard way.
if svc_cmd != '':
if svc_cmd != '':
# upstart or systemd
# upstart or systemd
rc1, stdout1, stderr1 = self.execute_command("%s %s %s" % (svc_cmd, 'stop', arguments), daemonize=True)
rc1, stdout1, stderr1 = self.execute_command("%s %s %s" % (svc_cmd, 'stop', arguments), daemonize=True)
@ -938,6 +990,7 @@ def main():
state = dict(choices=['running', 'started', 'stopped', 'restarted', 'reloaded']),
state = dict(choices=['running', 'started', 'stopped', 'restarted', 'reloaded']),
pattern = dict(required=False, default=None),
pattern = dict(required=False, default=None),
enabled = dict(choices=BOOLEANS, type='bool'),
enabled = dict(choices=BOOLEANS, type='bool'),
runlevel = dict(required=False, default='default'),
arguments = dict(aliases=['args'], default=''),
arguments = dict(aliases=['args'], default=''),
),
),
supports_check_mode=True
supports_check_mode=True