1 /*
2  * PROJECT:     ReactOS kernel-mode tests
3  * LICENSE:     LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
4  * PURPOSE:     Kernel-Mode Test Suite loader service control functions
5  * COPYRIGHT:   Copyright 2011-2018 Thomas Faber <thomas.faber@reactos.org>
6  *              Copyright 2017 Ged Murphy <gedmurphy@reactos.org>
7  *              Copyright 2018 Serge Gautherie <reactos-git_serge_171003@gautherie.fr>
8  */
9 
10 #include <kmt_test.h>
11 #include "kmtest.h"
12 
13 #include <assert.h>
14 
15 #define SERVICE_ACCESS (SERVICE_QUERY_STATUS | SERVICE_START | SERVICE_STOP | DELETE)
16 
17 /*
18  * This is an internal function not meant for use by the kmtests app,
19  * so we declare it here instead of kmtest.h
20  */
21 DWORD
22 KmtpCreateService(
23     IN PCWSTR ServiceName,
24     IN PCWSTR ServicePath,
25     IN PCWSTR DisplayName OPTIONAL,
26     IN DWORD ServiceType,
27     OUT SC_HANDLE *ServiceHandle);
28 
29 
30 static SC_HANDLE ScmHandle;
31 
32 /**
33  * @name KmtServiceInit
34  *
35  * Initialize service management routines (by opening the service control manager)
36  *
37  * @return Win32 error code
38  */
39 DWORD
40 KmtServiceInit(VOID)
41 {
42     DWORD Error = ERROR_SUCCESS;
43 
44     assert(!ScmHandle);
45 
46     ScmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
47     if (!ScmHandle)
48         error(Error);
49 
50     return Error;
51 }
52 
53 /**
54  * @name KmtServiceCleanup
55  *
56  * Clean up resources used by service management routines.
57  *
58  * @param IgnoreErrors
59  *        If TRUE, the function will never set ErrorLineAndFile, and always return ERROR_SUCCESS
60  *
61  * @return Win32 error code
62  */
63 DWORD
64 KmtServiceCleanup(
65     BOOLEAN IgnoreErrors)
66 {
67     DWORD Error = ERROR_SUCCESS;
68 
69     if (ScmHandle && !CloseServiceHandle(ScmHandle) && !IgnoreErrors)
70         error(Error);
71 
72     return Error;
73 }
74 
75 /**
76  * @name KmtCreateService
77  *
78  * Create the specified driver service and return a handle to it
79  *
80  * @param ServiceName
81  *        Name of the service to create
82  * @param ServicePath
83  *        File name of the driver, relative to the current directory
84  * @param DisplayName
85  *        Service display name
86  * @param ServiceHandle
87  *        Pointer to a variable to receive the handle to the service
88  *
89  * @return Win32 error code
90  */
91 DWORD
92 KmtCreateService(
93     IN PCWSTR ServiceName,
94     IN PCWSTR ServicePath,
95     IN PCWSTR DisplayName OPTIONAL,
96     OUT SC_HANDLE *ServiceHandle)
97 {
98     return KmtpCreateService(ServiceName,
99                              ServicePath,
100                              DisplayName,
101                              SERVICE_KERNEL_DRIVER,
102                              ServiceHandle);
103 }
104 
105 /**
106  * @name KmtGetServiceStateAsString
107  *
108  * @param ServiceState
109  *        Service state as a number
110  *
111  * @return Service state as a string
112  */
113 static
114 PCSTR
115 KmtGetServiceStateAsString(
116     IN DWORD ServiceState)
117 {
118     switch(ServiceState)
119     {
120         case SERVICE_STOPPED:
121             return "STOPPED";
122         case SERVICE_START_PENDING:
123             return "START_PENDING";
124         case SERVICE_STOP_PENDING:
125             return "STOP_PENDING";
126         case SERVICE_RUNNING:
127             return "RUNNING";
128         case SERVICE_CONTINUE_PENDING:
129             return "CONTINUE_PENDING";
130         case SERVICE_PAUSE_PENDING:
131             return "PAUSE_PENDING";
132         case SERVICE_PAUSED:
133             return "PAUSED";
134         default:
135             ok(FALSE, "Unknown service state = %lu\n", ServiceState);
136             return "(Unknown)";
137     }
138 }
139 
140 /**
141  * @name KmtEnsureServiceState
142  *
143  * @param ServiceName
144  *        Name of the service to check,
145  *        or NULL
146  * @param ServiceHandle
147  *        Handle to the service
148  * @param ExpectedServiceState
149  *        State which the service should be in
150  *
151  * @return Win32 error code
152  */
153 static
154 DWORD
155 KmtEnsureServiceState(
156     IN PCWSTR ServiceName OPTIONAL,
157     IN SC_HANDLE ServiceHandle,
158     IN DWORD ExpectedServiceState)
159 {
160     DWORD Error = ERROR_SUCCESS;
161     SERVICE_STATUS ServiceStatus;
162     DWORD StartTime = GetTickCount();
163     DWORD Timeout = 10 * 1000;
164     PCWSTR ServiceNameOut = ServiceName ? ServiceName : L"(handle only, no name)";
165 
166     assert(ServiceHandle);
167     assert(ExpectedServiceState);
168 
169     if (!QueryServiceStatus(ServiceHandle, &ServiceStatus))
170         error_goto(Error, cleanup);
171 
172     while (ServiceStatus.dwCurrentState != ExpectedServiceState)
173     {
174         // NB: ServiceStatus.dwWaitHint and ServiceStatus.dwCheckPoint logic could be added, if need be.
175 
176         Sleep(1 * 1000);
177 
178         if (!QueryServiceStatus(ServiceHandle, &ServiceStatus))
179             error_goto(Error, cleanup);
180 
181         if (GetTickCount() - StartTime >= Timeout)
182             break;
183     }
184 
185     if (ServiceStatus.dwCurrentState != ExpectedServiceState)
186     {
187         ok(FALSE, "Service = %ls, state = %lu %s (!= %lu %s), waitHint = %lu, checkPoint = %lu\n",
188            ServiceNameOut,
189            ServiceStatus.dwCurrentState, KmtGetServiceStateAsString(ServiceStatus.dwCurrentState),
190            ExpectedServiceState, KmtGetServiceStateAsString(ExpectedServiceState),
191            ServiceStatus.dwWaitHint, ServiceStatus.dwCheckPoint);
192         goto cleanup;
193     }
194 
195     trace("Service = %ls, state = %lu %s\n",
196           ServiceNameOut,
197           ExpectedServiceState, KmtGetServiceStateAsString(ExpectedServiceState));
198 
199 cleanup:
200     return Error;
201 }
202 
203 /**
204  * @name KmtStartService
205  *
206  * Start the specified driver service by handle or name (and return a handle to it)
207  *
208  * @param ServiceName
209  *        If *ServiceHandle is NULL, name of the service to start
210  * @param ServiceHandle
211  *        Pointer to a variable containing the service handle,
212  *        or NULL (in which case it will be filled with a handle to the service)
213  *
214  * @return Win32 error code
215  */
216 DWORD
217 KmtStartService(
218     IN PCWSTR ServiceName OPTIONAL,
219     IN OUT SC_HANDLE *ServiceHandle)
220 {
221     DWORD Error = ERROR_SUCCESS;
222 
223     assert(ServiceHandle);
224     assert(ServiceName || *ServiceHandle);
225 
226     if (!*ServiceHandle)
227         *ServiceHandle = OpenService(ScmHandle, ServiceName, SERVICE_ACCESS);
228 
229     if (!*ServiceHandle)
230         error_goto(Error, cleanup);
231 
232     if (!StartService(*ServiceHandle, 0, NULL))
233         error_goto(Error, cleanup);
234 
235     Error = KmtEnsureServiceState(ServiceName, *ServiceHandle, SERVICE_RUNNING);
236     if (Error)
237         goto cleanup;
238 
239 cleanup:
240     return Error;
241 }
242 
243 /**
244  * @name KmtCreateAndStartService
245  *
246  * Create and start the specified driver service and return a handle to it
247  *
248  * @param ServiceName
249  *        Name of the service to create
250  * @param ServicePath
251  *        File name of the driver, relative to the current directory
252  * @param DisplayName
253  *        Service display name
254  * @param ServiceHandle
255  *        Pointer to a variable to receive the handle to the service
256  * @param RestartIfRunning
257  *        TRUE to stop and restart the service if it is already running
258  *
259  * @return Win32 error code
260  */
261 DWORD
262 KmtCreateAndStartService(
263     IN PCWSTR ServiceName,
264     IN PCWSTR ServicePath,
265     IN PCWSTR DisplayName OPTIONAL,
266     OUT SC_HANDLE *ServiceHandle,
267     IN BOOLEAN RestartIfRunning)
268 {
269     DWORD Error = ERROR_SUCCESS;
270 
271     assert(ServiceHandle);
272 
273     Error = KmtCreateService(ServiceName, ServicePath, DisplayName, ServiceHandle);
274 
275     if (Error && Error != ERROR_SERVICE_EXISTS)
276         goto cleanup;
277 
278     Error = KmtStartService(ServiceName, ServiceHandle);
279 
280     if (Error != ERROR_SERVICE_ALREADY_RUNNING)
281         goto cleanup;
282 
283     Error = ERROR_SUCCESS;
284 
285     if (!RestartIfRunning)
286         goto cleanup;
287 
288     Error = KmtStopService(ServiceName, ServiceHandle);
289     if (Error)
290         goto cleanup;
291 
292     Error = KmtStartService(ServiceName, ServiceHandle);
293     if (Error)
294         goto cleanup;
295 
296 cleanup:
297     assert(Error || *ServiceHandle);
298     return Error;
299 }
300 
301 /**
302  * @name KmtStopService
303  *
304  * Stop the specified driver service by handle or name (and return a handle to it)
305  *
306  * @param ServiceName
307  *        If *ServiceHandle is NULL, name of the service to stop
308  * @param ServiceHandle
309  *        Pointer to a variable containing the service handle,
310  *        or NULL (in which case it will be filled with a handle to the service)
311  *
312  * @return Win32 error code
313  */
314 DWORD
315 KmtStopService(
316     IN PCWSTR ServiceName OPTIONAL,
317     IN OUT SC_HANDLE *ServiceHandle)
318 {
319     DWORD Error = ERROR_SUCCESS;
320     SERVICE_STATUS ServiceStatus;
321 
322     assert(ServiceHandle);
323     assert(ServiceName || *ServiceHandle);
324 
325     if (!*ServiceHandle)
326         *ServiceHandle = OpenService(ScmHandle, ServiceName, SERVICE_ACCESS);
327 
328     if (!*ServiceHandle)
329         error_goto(Error, cleanup);
330 
331     if (!ControlService(*ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus))
332         error_goto(Error, cleanup);
333 
334     Error = KmtEnsureServiceState(ServiceName, *ServiceHandle, SERVICE_STOPPED);
335     if (Error)
336         goto cleanup;
337 
338 cleanup:
339     return Error;
340 }
341 
342 /**
343  * @name KmtDeleteService
344  *
345  * Delete the specified driver service by handle or name (and return a handle to it)
346  *
347  * @param ServiceName
348  *        If *ServiceHandle is NULL, name of the service to delete
349  * @param ServiceHandle
350  *        Pointer to a variable containing the service handle.
351  *        Will be set to NULL on success
352  *
353  * @return Win32 error code
354  */
355 DWORD
356 KmtDeleteService(
357     IN PCWSTR ServiceName OPTIONAL,
358     IN OUT SC_HANDLE *ServiceHandle)
359 {
360     DWORD Error = ERROR_SUCCESS;
361 
362     assert(ServiceHandle);
363     assert(ServiceName || *ServiceHandle);
364 
365     if (!*ServiceHandle)
366         *ServiceHandle = OpenService(ScmHandle, ServiceName, SERVICE_ACCESS);
367 
368     if (!*ServiceHandle)
369         error_goto(Error, cleanup);
370 
371     if (!DeleteService(*ServiceHandle))
372         error_goto(Error, cleanup);
373 
374     if (*ServiceHandle)
375         CloseServiceHandle(*ServiceHandle);
376 
377 cleanup:
378     return Error;
379 }
380 
381 /**
382  * @name KmtCloseService
383  *
384  * Close the specified driver service handle
385  *
386  * @param ServiceHandle
387  *        Pointer to a variable containing the service handle.
388  *        Will be set to NULL on success
389  *
390  * @return Win32 error code
391  */
392 DWORD KmtCloseService(
393     IN OUT SC_HANDLE *ServiceHandle)
394 {
395     DWORD Error = ERROR_SUCCESS;
396 
397     assert(ServiceHandle);
398 
399     if (*ServiceHandle && !CloseServiceHandle(*ServiceHandle))
400         error_goto(Error, cleanup);
401 
402     *ServiceHandle = NULL;
403 
404 cleanup:
405     return Error;
406 }
407 
408 
409 /*
410  * Private function, not meant for use in kmtests
411  * See KmtCreateService & KmtFltCreateService
412  */
413 DWORD
414 KmtpCreateService(
415     IN PCWSTR ServiceName,
416     IN PCWSTR ServicePath,
417     IN PCWSTR DisplayName OPTIONAL,
418     IN DWORD ServiceType,
419     OUT SC_HANDLE *ServiceHandle)
420 {
421     DWORD Error = ERROR_SUCCESS;
422     WCHAR DriverPath[MAX_PATH];
423     HRESULT result = S_OK;
424 
425     assert(ServiceHandle);
426     assert(ServiceName && ServicePath);
427 
428     if (!GetModuleFileName(NULL, DriverPath, sizeof DriverPath / sizeof DriverPath[0]))
429         error_goto(Error, cleanup);
430 
431     assert(wcsrchr(DriverPath, L'\\') != NULL);
432     wcsrchr(DriverPath, L'\\')[1] = L'\0';
433 
434     result = StringCbCat(DriverPath, sizeof DriverPath, ServicePath);
435     if (FAILED(result))
436         error_value_goto(Error, result, cleanup);
437 
438     if (GetFileAttributes(DriverPath) == INVALID_FILE_ATTRIBUTES)
439         error_goto(Error, cleanup);
440 
441     *ServiceHandle = CreateService(ScmHandle, ServiceName, DisplayName,
442                                    SERVICE_ACCESS, ServiceType, SERVICE_DEMAND_START,
443                                    SERVICE_ERROR_NORMAL, DriverPath, NULL, NULL, NULL, NULL, NULL);
444 
445     if (!*ServiceHandle)
446         error_goto(Error, cleanup);
447 
448 cleanup:
449     return Error;
450 }
451