1import salt.utils.platform
2from salt.exceptions import CommandExecutionError
3
4try:
5    import pywintypes
6    import win32security
7    import win32service
8
9    HAS_WIN32 = True
10except ImportError:
11    HAS_WIN32 = False
12
13SERVICE_TYPE = {
14    1: "Kernel Driver",
15    2: "File System Driver",
16    4: "Adapter Driver",
17    8: "Recognizer Driver",
18    16: "Win32 Own Process",
19    32: "Win32 Share Process",
20    256: "Interactive",
21    "kernel": 1,
22    "filesystem": 2,
23    "adapter": 4,
24    "recognizer": 8,
25    "own": 16,
26    "share": 32,
27}
28
29SERVICE_CONTROLS = {
30    1: "Stop",
31    2: "Pause/Continue",
32    4: "Shutdown",
33    8: "Change Parameters",
34    16: "Netbind Change",
35    32: "Hardware Profile Change",
36    64: "Power Event",
37    128: "Session Change",
38    256: "Pre-Shutdown",
39    512: "Time Change",
40    1024: "Trigger Event",
41}
42
43SERVICE_STATE = {
44    1: "Stopped",
45    2: "Start Pending",
46    3: "Stop Pending",
47    4: "Running",
48    5: "Continue Pending",
49    6: "Pause Pending",
50    7: "Paused",
51}
52
53SERVICE_ERRORS = {0: "No Error", 1066: "Service Specific Error"}
54
55SERVICE_START_TYPE = {
56    "boot": 0,
57    "system": 1,
58    "auto": 2,
59    "manual": 3,
60    "disabled": 4,
61    0: "Boot",
62    1: "System",
63    2: "Auto",
64    3: "Manual",
65    4: "Disabled",
66}
67
68SERVICE_ERROR_CONTROL = {
69    0: "Ignore",
70    1: "Normal",
71    2: "Severe",
72    3: "Critical",
73    "ignore": 0,
74    "normal": 1,
75    "severe": 2,
76    "critical": 3,
77}
78
79__virtualname__ = "win_service"
80
81
82def __virtual__():
83    """
84    Only load if Win32 Libraries are installed
85    """
86    if not salt.utils.platform.is_windows():
87        return False, "win_dacl: Requires Windows"
88
89    if not HAS_WIN32:
90        return False, "win_dacl: Requires pywin32"
91
92    return __virtualname__
93
94
95def info(name):
96    """
97    Get information about a service on the system
98
99    Args:
100        name (str): The name of the service. This is not the display name. Use
101            ``get_service_name`` to find the service name.
102
103    Returns:
104        dict: A dictionary containing information about the service.
105
106    CLI Example:
107
108    .. code-block:: bash
109
110        salt '*' service.info spooler
111    """
112    try:
113        handle_scm = win32service.OpenSCManager(
114            None, None, win32service.SC_MANAGER_CONNECT
115        )
116    except pywintypes.error as exc:
117        raise CommandExecutionError(
118            "Failed to connect to the SCM: {}".format(exc.strerror)
119        )
120
121    try:
122        handle_svc = win32service.OpenService(
123            handle_scm,
124            name,
125            win32service.SERVICE_ENUMERATE_DEPENDENTS
126            | win32service.SERVICE_INTERROGATE
127            | win32service.SERVICE_QUERY_CONFIG
128            | win32service.SERVICE_QUERY_STATUS,
129        )
130    except pywintypes.error as exc:
131        raise CommandExecutionError("Failed To Open {}: {}".format(name, exc.strerror))
132
133    try:
134        config_info = win32service.QueryServiceConfig(handle_svc)
135        status_info = win32service.QueryServiceStatusEx(handle_svc)
136
137        try:
138            description = win32service.QueryServiceConfig2(
139                handle_svc, win32service.SERVICE_CONFIG_DESCRIPTION
140            )
141        except pywintypes.error:
142            description = "Failed to get description"
143
144        delayed_start = win32service.QueryServiceConfig2(
145            handle_svc, win32service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO
146        )
147    finally:
148        win32service.CloseServiceHandle(handle_scm)
149        win32service.CloseServiceHandle(handle_svc)
150
151    ret = dict()
152    try:
153        sid = win32security.LookupAccountName("", "NT Service\\{}".format(name))[0]
154        ret["sid"] = win32security.ConvertSidToStringSid(sid)
155    except pywintypes.error:
156        ret["sid"] = "Failed to get SID"
157
158    ret["BinaryPath"] = config_info[3]
159    ret["LoadOrderGroup"] = config_info[4]
160    ret["TagID"] = config_info[5]
161    ret["Dependencies"] = config_info[6]
162    ret["ServiceAccount"] = config_info[7]
163    ret["DisplayName"] = config_info[8]
164    ret["Description"] = description
165    ret["Status_ServiceCode"] = status_info["ServiceSpecificExitCode"]
166    ret["Status_CheckPoint"] = status_info["CheckPoint"]
167    ret["Status_WaitHint"] = status_info["WaitHint"]
168    ret["StartTypeDelayed"] = delayed_start
169
170    flags = list()
171    for bit in SERVICE_TYPE:
172        if isinstance(bit, int):
173            if config_info[0] & bit:
174                flags.append(SERVICE_TYPE[bit])
175
176    ret["ServiceType"] = flags if flags else config_info[0]
177
178    flags = list()
179    for bit in SERVICE_CONTROLS:
180        if status_info["ControlsAccepted"] & bit:
181            flags.append(SERVICE_CONTROLS[bit])
182
183    ret["ControlsAccepted"] = flags if flags else status_info["ControlsAccepted"]
184
185    try:
186        ret["Status_ExitCode"] = SERVICE_ERRORS[status_info["Win32ExitCode"]]
187    except KeyError:
188        ret["Status_ExitCode"] = status_info["Win32ExitCode"]
189
190    try:
191        ret["StartType"] = SERVICE_START_TYPE[config_info[1]]
192    except KeyError:
193        ret["StartType"] = config_info[1]
194
195    try:
196        ret["ErrorControl"] = SERVICE_ERROR_CONTROL[config_info[2]]
197    except KeyError:
198        ret["ErrorControl"] = config_info[2]
199
200    try:
201        ret["Status"] = SERVICE_STATE[status_info["CurrentState"]]
202    except KeyError:
203        ret["Status"] = status_info["CurrentState"]
204
205    return ret
206