Source code for windows.winobject.service

import ctypes
import windows

from collections import namedtuple
from contextlib import contextmanager

from windows import utils
from windows.pycompat import int_types
import windows.generated_def as gdef
from windows.generated_def import *
from windows import security
from windows.pycompat import basestring

"""
``type`` might be one of:

    * ``SERVICE_KERNEL_DRIVER(0x1L)``
    * ``SERVICE_FILE_SYSTEM_DRIVER(0x2L)``
    * ``SERVICE_WIN32_OWN_PROCESS(0x10L)``
    * ``SERVICE_WIN32_SHARE_PROCESS(0x20L)``
    * ``SERVICE_INTERACTIVE_PROCESS(0x100L)``

``state`` might be one of:

    * ``SERVICE_STOPPED(0x1L)``
    * ``SERVICE_START_PENDING(0x2L)``
    * ``SERVICE_STOP_PENDING(0x3L)``
    * ``SERVICE_RUNNING(0x4L)``
    * ``SERVICE_CONTINUE_PENDING(0x5L)``
    * ``SERVICE_PAUSE_PENDING(0x6L)``
    * ``SERVICE_PAUSED(0x7L)``

``flags`` might be one of:

    * ``0``
    * ``SERVICE_RUNS_IN_SYSTEM_PROCESS(0x1L)``

"""


[docs]class ServiceManager(utils.AutoHandle): """An object to query, list and explore services""" def _get_handle(self): return windows.winproxy.OpenSCManagerA(dwDesiredAccess=gdef.MAXIMUM_ALLOWED) def open_service(self, name, access=gdef.MAXIMUM_ALLOWED): return windows.winproxy.OpenServiceA(self.handle, name, access) # Check service exists :)
[docs] def get_service(self, key, access=gdef.MAXIMUM_ALLOWED): """Get a service by its name/index or a list of services via a slice :return: :class:`Service` or [:class:`Service`] -- A :class:`Service` or list of :class:`Service` """ if isinstance(key, int_types): return self.enumerate_services()[key] if isinstance(key, slice): # Get service list servlist = self.enumerate_services() # Extract indexes matching the slice indexes = key.indices(len(servlist)) return [servlist[idx] for idx in range(*indexes)] # Retrieve service by its name handle = self.open_service(key, access) return Service(name=key, handle=handle)
__getitem__ = get_service """Get a service by its name/index or a list of services via a slice :return: :class:`Service` or [:class:`Service`] -- A :class:`Service` or list of :class:`Service` """ def get_service_display_name(self, name): # This API is strange.. # Why can't we retrieve the display name for a service handle ? BUFFER_SIZE = 0x1000 result = (CHAR * BUFFER_SIZE)() size_needed = gdef.DWORD(BUFFER_SIZE) windows.winproxy.GetServiceDisplayNameA(self.handle, name, result, size_needed) return result.value def _enumerate_services_generator(self): """The generator code behind __iter__. Allow to iter over the services on the system """ size_needed = gdef.DWORD() nb_services = gdef.DWORD() counter = gdef.DWORD() try: windows.winproxy.EnumServicesStatusExA(self.handle, SC_ENUM_PROCESS_INFO, SERVICE_TYPE_ALL, SERVICE_STATE_ALL, None, 0, ctypes.byref(size_needed), ctypes.byref(nb_services), byref(counter), None) except WindowsError: pass while True: size = size_needed.value buffer = (BYTE * size)() try: windows.winproxy.EnumServicesStatusExA(self.handle, SC_ENUM_PROCESS_INFO, SERVICE_TYPE_ALL, SERVICE_STATE_ALL, buffer, size, ctypes.byref(size_needed), ctypes.byref(nb_services), byref(counter), None) except WindowsError as e: continue break services_array = (gdef.ENUM_SERVICE_STATUS_PROCESSA * nb_services.value).from_buffer(buffer) for service_info in services_array: shandle = self.open_service(service_info.lpServiceName) yield Service(handle=shandle, name=service_info.lpServiceName, description=service_info.lpDisplayName) return __iter__ = _enumerate_services_generator """Iter over the services on the system :yield: :class:`Service` """ def enumerate_services(self): return list(self._enumerate_services_generator())
[docs]class Service(gdef.SC_HANDLE): """Represent a service on the system""" def __init__(self, handle, name, description=None): super(Service, self).__init__(handle) self.name = name """The name of the service :type: :class:`str` """ if description is not None: self._description = description # Setup fixedpropety @property def description(self): """The description of the service :type: :class:`str` """ return ServiceManager().get_service_display_name(self.name) @property def status(self): """The status of the service :type: :class:`~windows.generated_def.winstructs.SERVICE_STATUS_PROCESS` """ buffer = windows.utils.BUFFER(gdef.SERVICE_STATUS_PROCESS)() size_needed = gdef.DWORD() windows.winproxy.QueryServiceStatusEx(self, gdef.SC_STATUS_PROCESS_INFO, buffer.cast(gdef.LPBYTE), ctypes.sizeof(buffer), size_needed) return buffer[0] @property # Can change if service is started/stopped when the object exist def process(self): """The process running the service (if any) :type: :class:`WinProcess <windows.winobject.process.WinProcess>` or ``None`` """ pid = self.status.dwProcessId if not pid: return None l = windows.WinProcess(pid=pid) return l @property def security_descriptor(self): """The security descriptor of the service :type: :class:`~windows.security.SecurityDescriptor` """ return security.SecurityDescriptor.from_service(self.name)
[docs] def start(self, args=None): """Start the service :param args: a list of :class:`str` """ nbelt = 0 if args is not None: if isinstance(args, windows.pycompat.anybuff): args = [args] nbelt = len(args) args = (gdef.LPCSTR * (nbelt))(*args) return windows.winproxy.StartServiceA(self, nbelt, args)
[docs] def stop(self): """Stop the service""" status = SERVICE_STATUS() windows.winproxy.ControlService(self, gdef.SERVICE_CONTROL_STOP, status) return status
def __repr__(self): return """<{0} "{1}" {2!r}>""".format(type(self).__name__, self.name, self.status.state) def __del__(self): return windows.winproxy.CloseServiceHandle(self)