Mini Shell
"""
Service support for RHEL-based systems, including support for both upstart and sysvinit
.. important::
If you feel that Salt should be using this module to manage services on a
minion, and it is using a different module (or gives an error similar to
*'service.start' is not available*), see :ref:`here
<module-provider-override>`.
"""
import fnmatch
import glob
import logging
import os
import re
import stat
import salt.utils.path
log = logging.getLogger(__name__)
__func_alias__ = {"reload_": "reload"}
# Define the module's virtual name
__virtualname__ = "service"
# Import upstart module if needed
HAS_UPSTART = False
if salt.utils.path.which("initctl"):
try:
# Don't re-invent the wheel, import the helper functions from the
# upstart module.
from salt.modules.upstart_service import (
_upstart_disable,
_upstart_enable,
_upstart_is_enabled,
)
except Exception as exc: # pylint: disable=broad-except
log.error(
"Unable to import helper functions from salt.modules.upstart: %s", exc
)
else:
HAS_UPSTART = True
def __virtual__():
"""
Only work on select distros which still use Red Hat's /usr/bin/service for
management of either sysvinit or a hybrid sysvinit/upstart init system.
"""
# Disable when booted with systemd
if __utils__["systemd.booted"](__context__):
return (
False,
"The rh_service execution module failed to load: this system was booted"
" with systemd.",
)
# Enable on these platforms only.
enable = {
"XenServer",
"XCP-ng",
"RedHat",
"CentOS",
"ScientificLinux",
"CloudLinux",
"Amazon",
"Fedora",
"ALT",
"OEL",
"SUSE Enterprise Server",
"SUSE",
"McAfee OS Server",
"VirtuozzoLinux",
}
if __grains__["os"] in enable:
if __grains__["os"] == "SUSE":
if str(__grains__["osrelease"]).startswith("11"):
return __virtualname__
else:
return (False, "Cannot load rh_service module on SUSE > 11")
osrelease_major = __grains__.get("osrelease_info", [0])[0]
if __grains__["os"] in ("XenServer", "XCP-ng"):
if osrelease_major >= 7:
return (
False,
"XenServer and XCP-ng >= 7 use systemd, will not load rh_service.py"
" as virtual 'service'",
)
return __virtualname__
if __grains__["os"] == "Fedora":
if osrelease_major >= 15:
return (
False,
"Fedora >= 15 uses systemd, will not load rh_service.py "
"as virtual 'service'",
)
if __grains__["os"] in (
"RedHat",
"CentOS",
"ScientificLinux",
"OEL",
"CloudLinux",
):
if osrelease_major >= 7:
return (
False,
"RedHat-based distros >= version 7 use systemd, will not "
"load rh_service.py as virtual 'service'",
)
return __virtualname__
return (False, f"Cannot load rh_service module: OS not in {enable}")
def _runlevel():
"""
Return the current runlevel
"""
out = __salt__["cmd.run"]("/sbin/runlevel")
# unknown will be returned while inside a kickstart environment, since
# this is usually a server deployment it should be safe to assume runlevel
# 3. If not all service related states will throw an out of range
# exception here which will cause other functions to fail.
if "unknown" in out:
return "3"
else:
return out.split()[1]
def _chkconfig_add(name):
"""
Run 'chkconfig --add' for a service whose script is installed in
/etc/init.d. The service is initially configured to be disabled at all
run-levels.
"""
cmd = f"/sbin/chkconfig --add {name}"
if __salt__["cmd.retcode"](cmd, python_shell=False) == 0:
log.info('Added initscript "%s" to chkconfig', name)
return True
else:
log.error('Unable to add initscript "%s" to chkconfig', name)
return False
def _service_is_upstart(name):
"""
Return True if the service is an upstart service, otherwise return False.
"""
return HAS_UPSTART and os.path.exists(f"/etc/init/{name}.conf")
def _service_is_sysv(name):
"""
Return True if the service is a System V service (includes those managed by
chkconfig); otherwise return False.
"""
try:
# Look for user-execute bit in file mode.
return bool(os.stat(os.path.join("/etc/init.d", name)).st_mode & stat.S_IXUSR)
except OSError:
return False
def _service_is_chkconfig(name):
"""
Return True if the service is managed by chkconfig.
"""
cmdline = f"/sbin/chkconfig --list {name}"
return (
__salt__["cmd.retcode"](cmdline, python_shell=False, ignore_retcode=True) == 0
)
def _sysv_is_enabled(name, runlevel=None):
"""
Return True if the sysv (or chkconfig) service is enabled for the specified
runlevel; otherwise return False. If `runlevel` is None, then use the
current runlevel.
"""
# Try chkconfig first.
result = _chkconfig_is_enabled(name, runlevel)
if result:
return True
if runlevel is None:
runlevel = _runlevel()
return len(glob.glob(f"/etc/rc.d/rc{runlevel}.d/S??{name}")) > 0
def _chkconfig_is_enabled(name, runlevel=None):
"""
Return ``True`` if the service is enabled according to chkconfig; otherwise
return ``False``. If ``runlevel`` is ``None``, then use the current
runlevel.
"""
cmdline = f"/sbin/chkconfig --list {name}"
result = __salt__["cmd.run_all"](cmdline, python_shell=False)
if runlevel is None:
runlevel = _runlevel()
if result["retcode"] == 0:
for row in result["stdout"].splitlines():
if f"{runlevel}:on" in row:
if row.split()[0] == name:
return True
elif row.split() == [name, "on"]:
return True
return False
def _sysv_enable(name):
"""
Enable the named sysv service to start at boot. The service will be enabled
using chkconfig with default run-levels if the service is chkconfig
compatible. If chkconfig is not available, then this will fail.
"""
if not _service_is_chkconfig(name) and not _chkconfig_add(name):
return False
cmd = f"/sbin/chkconfig {name} on"
return not __salt__["cmd.retcode"](cmd, python_shell=False)
def _sysv_disable(name):
"""
Disable the named sysv service from starting at boot. The service will be
disabled using chkconfig with default run-levels if the service is chkconfig
compatible; otherwise, the service will be disabled for the current
run-level only.
"""
if not _service_is_chkconfig(name) and not _chkconfig_add(name):
return False
cmd = f"/sbin/chkconfig {name} off"
return not __salt__["cmd.retcode"](cmd, python_shell=False)
def _sysv_delete(name):
"""
Delete the named sysv service from the system. The service will be
deleted using chkconfig.
"""
if not _service_is_chkconfig(name):
return False
cmd = f"/sbin/chkconfig --del {name}"
return not __salt__["cmd.retcode"](cmd)
def _upstart_delete(name):
"""
Delete an upstart service. This will only rename the .conf file
"""
if HAS_UPSTART:
if os.path.exists(f"/etc/init/{name}.conf"):
os.rename(
f"/etc/init/{name}.conf",
f"/etc/init/{name}.conf.removed",
)
return True
def _upstart_services():
"""
Return list of upstart services.
"""
if HAS_UPSTART:
return [os.path.basename(name)[:-5] for name in glob.glob("/etc/init/*.conf")]
else:
return []
def _sysv_services():
"""
Return list of sysv services.
"""
_services = []
output = __salt__["cmd.run"](["chkconfig", "--list"], python_shell=False)
for line in output.splitlines():
comps = line.split()
try:
if comps[1].startswith("0:"):
_services.append(comps[0])
except IndexError:
continue
# Return only the services that have an initscript present
return [x for x in _services if _service_is_sysv(x)]
def get_enabled(limit=""):
"""
Return the enabled services. Use the ``limit`` param to restrict results
to services of that type.
CLI Examples:
.. code-block:: bash
salt '*' service.get_enabled
salt '*' service.get_enabled limit=upstart
salt '*' service.get_enabled limit=sysvinit
"""
limit = limit.lower()
if limit == "upstart":
return sorted(name for name in _upstart_services() if _upstart_is_enabled(name))
elif limit == "sysvinit":
runlevel = _runlevel()
return sorted(
name for name in _sysv_services() if _sysv_is_enabled(name, runlevel)
)
else:
runlevel = _runlevel()
return sorted(
[name for name in _upstart_services() if _upstart_is_enabled(name)]
+ [name for name in _sysv_services() if _sysv_is_enabled(name, runlevel)]
)
def get_disabled(limit=""):
"""
Return the disabled services. Use the ``limit`` param to restrict results
to services of that type.
CLI Example:
.. code-block:: bash
salt '*' service.get_disabled
salt '*' service.get_disabled limit=upstart
salt '*' service.get_disabled limit=sysvinit
"""
limit = limit.lower()
if limit == "upstart":
return sorted(
name for name in _upstart_services() if not _upstart_is_enabled(name)
)
elif limit == "sysvinit":
runlevel = _runlevel()
return sorted(
name for name in _sysv_services() if not _sysv_is_enabled(name, runlevel)
)
else:
runlevel = _runlevel()
return sorted(
[name for name in _upstart_services() if not _upstart_is_enabled(name)]
+ [
name
for name in _sysv_services()
if not _sysv_is_enabled(name, runlevel)
]
)
def get_all(limit=""):
"""
Return all installed services. Use the ``limit`` param to restrict results
to services of that type.
CLI Example:
.. code-block:: bash
salt '*' service.get_all
salt '*' service.get_all limit=upstart
salt '*' service.get_all limit=sysvinit
"""
limit = limit.lower()
if limit == "upstart":
return sorted(_upstart_services())
elif limit == "sysvinit":
return sorted(_sysv_services())
else:
return sorted(_sysv_services() + _upstart_services())
def available(name, limit=""):
"""
Return True if the named service is available. Use the ``limit`` param to
restrict results to services of that type.
CLI Examples:
.. code-block:: bash
salt '*' service.available sshd
salt '*' service.available sshd limit=upstart
salt '*' service.available sshd limit=sysvinit
"""
if limit == "upstart":
return _service_is_upstart(name)
elif limit == "sysvinit":
return _service_is_sysv(name)
else:
return (
_service_is_upstart(name)
or _service_is_sysv(name)
or _service_is_chkconfig(name)
)
def missing(name, limit=""):
"""
The inverse of service.available.
Return True if the named service is not available. Use the ``limit`` param to
restrict results to services of that type.
CLI Examples:
.. code-block:: bash
salt '*' service.missing sshd
salt '*' service.missing sshd limit=upstart
salt '*' service.missing sshd limit=sysvinit
"""
if limit == "upstart":
return not _service_is_upstart(name)
elif limit == "sysvinit":
return not _service_is_sysv(name)
else:
if _service_is_upstart(name) or _service_is_sysv(name):
return False
else:
return True
def start(name):
"""
Start the specified service
CLI Example:
.. code-block:: bash
salt '*' service.start <service name>
"""
if _service_is_upstart(name):
cmd = f"start {name}"
else:
cmd = f"/sbin/service {name} start"
return not __salt__["cmd.retcode"](cmd, python_shell=False)
def stop(name):
"""
Stop the specified service
CLI Example:
.. code-block:: bash
salt '*' service.stop <service name>
"""
if _service_is_upstart(name):
cmd = f"stop {name}"
else:
cmd = f"/sbin/service {name} stop"
return not __salt__["cmd.retcode"](cmd, python_shell=False)
def restart(name):
"""
Restart the named service
CLI Example:
.. code-block:: bash
salt '*' service.restart <service name>
"""
if _service_is_upstart(name):
cmd = f"restart {name}"
else:
cmd = f"/sbin/service {name} restart"
return not __salt__["cmd.retcode"](cmd, python_shell=False)
def reload_(name):
"""
Reload the named service
CLI Example:
.. code-block:: bash
salt '*' service.reload <service name>
"""
if _service_is_upstart(name):
cmd = f"reload {name}"
else:
cmd = f"/sbin/service {name} reload"
return not __salt__["cmd.retcode"](cmd, python_shell=False)
def status(name, sig=None):
"""
Return the status for a service.
If the name contains globbing, a dict mapping service name to True/False
values is returned.
.. versionchanged:: 2018.3.0
The service name can now be a glob (e.g. ``salt*``)
Args:
name (str): The name of the service to check
sig (str): Signature to use to find the service via ps
Returns:
bool: True if running, False otherwise
dict: Maps service name to True if running, False otherwise
CLI Example:
.. code-block:: bash
salt '*' service.status <service name> [service signature]
"""
if sig:
return bool(__salt__["status.pid"](sig))
contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name))
if contains_globbing:
services = fnmatch.filter(get_all(), name)
else:
services = [name]
results = {}
for service in services:
if _service_is_upstart(service):
cmd = f"status {service}"
results[service] = "start/running" in __salt__["cmd.run"](
cmd, python_shell=False
)
else:
cmd = f"/sbin/service {service} status"
results[service] = (
__salt__["cmd.retcode"](cmd, python_shell=False, ignore_retcode=True)
== 0
)
if contains_globbing:
return results
return results[name]
def delete(name, **kwargs):
"""
Delete the named service
.. versionadded:: 2016.3.0
CLI Example:
.. code-block:: bash
salt '*' service.delete <service name>
"""
if _service_is_upstart(name):
return _upstart_delete(name)
else:
return _sysv_delete(name)
def enable(name, **kwargs):
"""
Enable the named service to start at boot
CLI Example:
.. code-block:: bash
salt '*' service.enable <service name>
"""
if _service_is_upstart(name):
return _upstart_enable(name)
else:
return _sysv_enable(name)
def disable(name, **kwargs):
"""
Disable the named service to start at boot
CLI Example:
.. code-block:: bash
salt '*' service.disable <service name>
"""
if _service_is_upstart(name):
return _upstart_disable(name)
else:
return _sysv_disable(name)
def enabled(name, **kwargs):
"""
Check to see if the named service is enabled to start on boot
CLI Example:
.. code-block:: bash
salt '*' service.enabled <service name>
"""
if _service_is_upstart(name):
return _upstart_is_enabled(name)
else:
return _sysv_is_enabled(name)
def disabled(name):
"""
Check to see if the named service is disabled to start on boot
CLI Example:
.. code-block:: bash
salt '*' service.disabled <service name>
"""
if _service_is_upstart(name):
return not _upstart_is_enabled(name)
else:
return not _sysv_is_enabled(name)
Zerion Mini Shell 1.0