PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/mac_service.py |
""" The service module for macOS .. versionadded:: 2016.3.0 This module has support for services in the following locations. .. code-block:: bash /System/Library/LaunchDaemons/ /System/Library/LaunchAgents/ /Library/LaunchDaemons/ /Library/LaunchAgents/ # As of version "2019.2.0" support for user-specific services were added. /Users/foo/Library/LaunchAgents/ .. note:: As of the 2019.2.0 release, if a service is located in a ``LaunchAgent`` path and a ``runas`` user is NOT specified, the current console user will be used to properly interact with the service. .. note:: As of the 3002 release, if a service name of ``salt-minion`` is passed this module will convert it over to it's macOS equivalent name, in this case to ``com.saltstack.salt.minion``. This is true for ``salt-master`` ``salt-api``, and ``salt-syndic`` as well. """ import logging import os import salt.utils.files import salt.utils.path import salt.utils.platform import salt.utils.stringutils from salt.exceptions import CommandExecutionError from salt.utils.versions import Version # Define the module's virtual name __virtualname__ = "service" __func_alias__ = { "list_": "list", } log = logging.getLogger(__name__) SALT_MAC_SERVICES = { "salt-minion": "com.saltstack.salt.minion", "salt-master": "com.saltstack.salt.master", "salt-api": "com.saltstack.salt.api", "salt-syndic": "com.saltstack.salt.syndic", } def __virtual__(): """ Only for macOS with launchctl """ if not salt.utils.platform.is_darwin(): return ( False, "Failed to load the mac_service module:\nOnly available on macOS systems.", ) if not salt.utils.path.which("launchctl"): return ( False, "Failed to load the mac_service module:\n" 'Required binary not found: "launchctl"', ) if not salt.utils.path.which("plutil"): return ( False, "Failed to load the mac_service module:\n" 'Required binary not found: "plutil"', ) if Version(__grains__["osrelease"]) < Version("10.11"): return ( False, "Failed to load the mac_service module:\nRequires macOS 10.11 or newer", ) return __virtualname__ def _name_in_services(name, services): """ Checks to see if the given service is in the given services. :param str name: Service label, file name, or full path :param dict services: The currently available services. :return: The service information for the service, otherwise an empty dictionary :rtype: dict """ if name in services: # Match on label return services[name] for service in services.values(): if service["file_path"].lower() == name: # Match on full path return service basename, ext = os.path.splitext(service["file_name"]) if basename.lower() == name: # Match on basename return service return dict() def _get_service(name): """ Get information about a service. If the service is not found, raise an error :param str name: Service label, file name, or full path :return: The service information for the service, otherwise an Error :rtype: dict """ services = __utils__["mac_utils.available_services"]() # fix the name differences between platforms # salt-minion becomes com.saltstack.salt.minion name = SALT_MAC_SERVICES.get(name, name).lower() service = _name_in_services(name, services) # if we would the service we can return it if service: return service # if we got here our service is not available, now we can check to see if # we received a cached batch of services, if not we did a fresh check # so we need to raise that the service could not be found. try: if not __context__["using_cached_services"]: raise CommandExecutionError(f"Service not found: {name}") except KeyError: pass # if we can't find a service and we are being run from a service.dead # state then there is no reason to check again. # fixes https://github.com/saltstack/salt/issues/57907 if __context__.get("service.state") == "dead": raise CommandExecutionError(f"Service not found: {name}") # we used a cached version to check, a service could have been made # between now and then, we should refresh our available services. services = __utils__["mac_utils.available_services"](refresh=True) # check to see if we found the service we are looking for. service = _name_in_services(name, services) if not service: # Could not find the service after refresh raise. raise CommandExecutionError(f"Service not found: {name}") # found it :) return service def _always_running_service(name): """ Check if the service should always be running based on the KeepAlive Key in the service plist. :param str name: Service label, file name, or full path :return: True if the KeepAlive key is set to True, False if set to False or not set in the plist at all. :rtype: bool .. versionadded:: 2019.2.0 """ # get all the info from the launchctl service service_info = show(name) # get the value for the KeepAlive key in service plist try: keep_alive = service_info["plist"]["KeepAlive"] except KeyError: return False # check if KeepAlive is True and not just set. if isinstance(keep_alive, dict): # check for pathstate for _file, value in keep_alive.get("PathState", {}).items(): if value is True and os.path.exists(_file): return True elif value is False and not os.path.exists(_file): return True if keep_alive is True: return True return False def _get_domain_target(name, service_target=False): """ Returns the domain/service target and path for a service. This is used to determine whether or not a service should be loaded in a user space or system space. :param str name: Service label, file name, or full path :param bool service_target: Whether to return a full service target. This is needed for the enable and disable subcommands of /bin/launchctl. Defaults to False :return: Tuple of the domain/service target and the path to the service. :rtype: tuple .. versionadded:: 2019.2.0 """ # Get service information service = _get_service(name) # get the path to the service path = service["file_path"] # most of the time we'll be at the system level. domain_target = "system" # check if a LaunchAgent as we should treat these differently. if "LaunchAgents" in path: # Get the console user so we can service in the correct session uid = __utils__["mac_utils.console_user"]() domain_target = f"gui/{uid}" # check to see if we need to make it a full service target. if service_target is True: domain_target = "{}/{}".format(domain_target, service["plist"]["Label"]) return (domain_target, path) def _launch_agent(name): """ Checks to see if the provided service is a LaunchAgent :param str name: Service label, file name, or full path :return: True if a LaunchAgent, False if not. :rtype: bool .. versionadded:: 2019.2.0 """ # Get the path to the service. path = _get_service(name)["file_path"] if "LaunchAgents" not in path: return False return True def show(name): """ Show properties of a launchctl service :param str name: Service label, file name, or full path :return: The service information if the service is found :rtype: dict CLI Example: .. code-block:: bash salt '*' service.show org.cups.cupsd # service label salt '*' service.show org.cups.cupsd.plist # file name salt '*' service.show /System/Library/LaunchDaemons/org.cups.cupsd.plist # full path """ return _get_service(name) def launchctl(sub_cmd, *args, **kwargs): """ Run a launchctl command and raise an error if it fails :param str sub_cmd: Sub command supplied to launchctl :param tuple args: Tuple containing additional arguments to pass to launchctl :param dict kwargs: Dictionary containing arguments to pass to ``cmd.run_all`` :param bool return_stdout: A keyword argument. If true return the stdout of the launchctl command :return: ``True`` if successful, raise ``CommandExecutionError`` if not, or the stdout of the launchctl command if requested :rtype: bool, str CLI Example: .. code-block:: bash salt '*' service.launchctl debug org.cups.cupsd """ return __utils__["mac_utils.launchctl"](sub_cmd, *args, **kwargs) def list_(name=None, runas=None): """ Run launchctl list and return the output :param str name: The name of the service to list :param str runas: User to run launchctl commands :return: If a name is passed returns information about the named service, otherwise returns a list of all services and pids :rtype: str CLI Example: .. code-block:: bash salt '*' service.list salt '*' service.list org.cups.cupsd """ if name: # Get service information and label service = _get_service(name) label = service["plist"]["Label"] # we can assume if we are trying to list a LaunchAgent we need # to run as a user, if not provided, we'll use the console user. if not runas and _launch_agent(name): runas = __utils__["mac_utils.console_user"](username=True) # Collect information on service: will raise an error if it fails return launchctl("list", label, return_stdout=True, runas=runas) # Collect information on all services: will raise an error if it fails return launchctl("list", return_stdout=True, runas=runas) def enable(name, runas=None): """ Enable a launchd service. Raises an error if the service fails to be enabled :param str name: Service label, file name, or full path :param str runas: User to run launchctl commands :return: ``True`` if successful or if the service is already enabled :rtype: bool CLI Example: .. code-block:: bash salt '*' service.enable org.cups.cupsd """ # Get the domain target. enable requires a full <service-target> service_target = _get_domain_target(name, service_target=True)[0] # Enable the service: will raise an error if it fails return launchctl("enable", service_target, runas=runas) def disable(name, runas=None): """ Disable a launchd service. Raises an error if the service fails to be disabled :param str name: Service label, file name, or full path :param str runas: User to run launchctl commands :return: ``True`` if successful or if the service is already disabled :rtype: bool CLI Example: .. code-block:: bash salt '*' service.disable org.cups.cupsd """ # Get the service target. enable requires a full <service-target> service_target = _get_domain_target(name, service_target=True)[0] # disable the service: will raise an error if it fails return launchctl("disable", service_target, runas=runas) def start(name, runas=None): """ Start a launchd service. Raises an error if the service fails to start .. note:: To start a service in macOS the service must be enabled first. Use ``service.enable`` to enable the service. :param str name: Service label, file name, or full path :param str runas: User to run launchctl commands :return: ``True`` if successful or if the service is already running :rtype: bool CLI Example: .. code-block:: bash salt '*' service.start org.cups.cupsd """ # Get the domain target. domain_target, path = _get_domain_target(name) # Load (bootstrap) the service: will raise an error if it fails return launchctl("bootstrap", domain_target, path, runas=runas) def stop(name, runas=None): """ Stop a launchd service. Raises an error if the service fails to stop .. note:: Though ``service.stop`` will unload a service in macOS, the service will start on next boot unless it is disabled. Use ``service.disable`` to disable the service :param str name: Service label, file name, or full path :param str runas: User to run launchctl commands :return: ``True`` if successful or if the service is already stopped :rtype: bool CLI Example: .. code-block:: bash salt '*' service.stop org.cups.cupsd """ # Get the domain target. domain_target, path = _get_domain_target(name) # Stop (bootout) the service: will raise an error if it fails return launchctl("bootout", domain_target, path, runas=runas) def restart(name, runas=None): """ Unloads and reloads a launchd service. Raises an error if the service fails to reload :param str name: Service label, file name, or full path :param str runas: User to run launchctl commands :return: ``True`` if successful :rtype: bool CLI Example: .. code-block:: bash salt '*' service.restart org.cups.cupsd """ # Restart the service: will raise an error if it fails if __salt__["service.loaded"](name, runas=runas): __salt__["service.stop"](name, runas=runas) return __salt__["service.start"](name, runas=runas) def status(name, sig=None, runas=None): """ Return the status for a service. .. note:: Previously this function would return a PID for a running service with a PID or 'loaded' for a loaded service without a PID. This was changed to have better parity with other service modules that return True/False. :param str name: Used to find the service from launchctl. Can be the service Label, file name, or path to the service file. (normally a plist) :param str sig: Find the service with status.pid instead. Note that ``name`` must still be provided. :param str runas: User to run launchctl commands. :return: True if running, otherwise False. :rtype: str CLI Example: .. code-block:: bash salt '*' service.status cups """ # Find service with ps if sig: return __salt__["status.pid"](sig) try: _get_service(name) except CommandExecutionError as msg: log.error(msg) return False if not runas and _launch_agent(name): runas = __utils__["mac_utils.console_user"](username=True) try: output = __salt__["service.list"](name, runas=runas) except CommandExecutionError: return False # we should only check for a PID if it's supposed to have one. # If we can't find a PID then something is wrong with the service. if _always_running_service(name): return True if '"PID" =' in output else False return True def available(name): """ Check that the given service is available. :param str name: The name of the service :return: True if the service is available, otherwise False :rtype: bool CLI Example: .. code-block:: bash salt '*' service.available com.openssh.sshd """ try: _get_service(name) return True except CommandExecutionError: return False def missing(name): """ The inverse of service.available Check that the given service is not available. :param str name: The name of the service :return: True if the service is not available, otherwise False :rtype: bool CLI Example: .. code-block:: bash salt '*' service.missing com.openssh.sshd """ return not available(name) def enabled(name, runas=None): """ Check if the specified service is enabled (not disabled, capable of being loaded/bootstrapped). .. note:: Previously this function would see if the service is loaded via ``launchctl list`` to determine if the service is enabled. This was not an accurate way to do so. The new behavior checks to make sure its not disabled to determine the status. Please use ``service.loaded`` for the previous behavior. :param str name: The name of the service to look up. :param str runas: User to run launchctl commands. :return: True if the specified service enabled, otherwise False :rtype: bool CLI Example: .. code-block:: bash salt '*' service.enabled org.cups.cupsd """ # There isn't a direct way to get enabled, but if its not disabled # then its enabled. return not __salt__["service.disabled"](name, runas) def disabled(name, runas=None, domain="system"): """ Check if the specified service is not enabled. This is the opposite of ``service.enabled`` :param str name: The name to look up :param str runas: User to run launchctl commands :param str domain: domain to check for disabled services. Default is system. :return: True if the specified service is NOT enabled, otherwise False :rtype: bool CLI Example: .. code-block:: bash salt '*' service.disabled org.cups.cupsd """ domain = _get_domain_target(name, service_target=True)[0] disabled = launchctl("print-disabled", domain, return_stdout=True, runas=runas) for service in disabled.split("\n"): if name in service: srv_name = service.split("=>")[0].split('"')[1] status = service.split("=>")[1] if name != srv_name: pass else: matches = ["true", "disabled"] return True if any([x in status.lower() for x in matches]) else False return False def get_all(runas=None): """ Return a list of services that are enabled or available. Can be used to find the name of a service. :param str runas: User to run launchctl commands :return: A list of all the services available or enabled :rtype: list CLI Example: .. code-block:: bash salt '*' service.get_all """ # Get list of enabled services enabled = get_enabled(runas=runas) # Get list of all services available = list(__utils__["mac_utils.available_services"]().keys()) # Return composite list return sorted(set(enabled + available)) def get_enabled(runas=None): """ Return a list of all services that are enabled. Can be used to find the name of a service. :param str runas: User to run launchctl commands :return: A list of all the services enabled on the system :rtype: list CLI Example: .. code-block:: bash salt '*' service.get_enabled """ # Collect list of enabled services stdout = list_(runas=runas) service_lines = [line for line in stdout.splitlines()] # Construct list of enabled services enabled = [] for line in service_lines: # Skip header line if line.startswith("PID"): continue pid, status, label = line.split("\t") enabled.append(label) return sorted(set(enabled)) def loaded(name, runas=None): """ Check if the specified service is loaded. :param str name: The name of the service to look up :param str runas: User to run launchctl commands :return: ``True`` if the specified service is loaded, otherwise ``False`` :rtype: bool CLI Example: .. code-block:: bash salt '*' service.loaded org.cups.cupsd """ # Try to list the service. If it can't be listed, it's not enabled try: __salt__["service.list"](name=name, runas=runas) return True except CommandExecutionError: return False