Reactos
1/*
2 * PROJECT: ReactOS Service Control Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/system/services/driver.c
5 * PURPOSE: Driver control interface
6 * COPYRIGHT: Copyright 2005-2006 Eric Kohl
7 *
8 */
9
10/* INCLUDES *****************************************************************/
11
12#include "services.h"
13
14#include <ndk/iofuncs.h>
15#include <ndk/setypes.h>
16
17#define NDEBUG
18#include <debug.h>
19
20/* FUNCTIONS ****************************************************************/
21
22static
23DWORD
24ScmLoadDriver(PSERVICE lpService)
25{
26 NTSTATUS Status = STATUS_SUCCESS;
27 BOOLEAN WasPrivilegeEnabled = FALSE;
28 PWSTR pszDriverPath;
29 UNICODE_STRING DriverPath;
30
31 /* Build the driver path */
32 /* 52 = wcslen(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") */
33 pszDriverPath = HeapAlloc(GetProcessHeap(),
34 HEAP_ZERO_MEMORY,
35 (52 + wcslen(lpService->lpServiceName) + 1) * sizeof(WCHAR));
36 if (pszDriverPath == NULL)
37 return ERROR_NOT_ENOUGH_MEMORY;
38
39 wcscpy(pszDriverPath,
40 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
41 wcscat(pszDriverPath,
42 lpService->lpServiceName);
43
44 RtlInitUnicodeString(&DriverPath,
45 pszDriverPath);
46
47 DPRINT(" Path: %wZ\n", &DriverPath);
48
49 /* Acquire driver-loading privilege */
50 Status = RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE,
51 TRUE,
52 FALSE,
53 &WasPrivilegeEnabled);
54 if (!NT_SUCCESS(Status))
55 {
56 /* We encountered a failure, exit properly */
57 DPRINT1("SERVICES: Cannot acquire driver-loading privilege, Status = 0x%08lx\n", Status);
58 goto done;
59 }
60
61 Status = NtLoadDriver(&DriverPath);
62
63 /* Release driver-loading privilege */
64 RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE,
65 WasPrivilegeEnabled,
66 FALSE,
67 &WasPrivilegeEnabled);
68
69done:
70 HeapFree(GetProcessHeap(), 0, pszDriverPath);
71 return RtlNtStatusToDosError(Status);
72}
73
74
75static
76DWORD
77ScmUnloadDriver(PSERVICE lpService)
78{
79 NTSTATUS Status = STATUS_SUCCESS;
80 BOOLEAN WasPrivilegeEnabled = FALSE;
81 PWSTR pszDriverPath;
82 UNICODE_STRING DriverPath;
83 DWORD dwError;
84
85 /* Build the driver path */
86 /* 52 = wcslen(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") */
87 pszDriverPath = HeapAlloc(GetProcessHeap(),
88 HEAP_ZERO_MEMORY,
89 (52 + wcslen(lpService->lpServiceName) + 1) * sizeof(WCHAR));
90 if (pszDriverPath == NULL)
91 return ERROR_NOT_ENOUGH_MEMORY;
92
93 wcscpy(pszDriverPath,
94 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
95 wcscat(pszDriverPath,
96 lpService->lpServiceName);
97
98 RtlInitUnicodeString(&DriverPath,
99 pszDriverPath);
100
101 DPRINT(" Path: %wZ\n", &DriverPath);
102
103 /* Acquire driver-unloading privilege */
104 Status = RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE,
105 TRUE,
106 FALSE,
107 &WasPrivilegeEnabled);
108 if (!NT_SUCCESS(Status))
109 {
110 /* We encountered a failure, exit properly */
111 DPRINT1("SERVICES: Cannot acquire driver-unloading privilege, Status = 0x%08lx\n", Status);
112 dwError = RtlNtStatusToDosError(Status);
113 goto done;
114 }
115
116 Status = NtUnloadDriver(&DriverPath);
117 if (Status == STATUS_INVALID_DEVICE_REQUEST)
118 dwError = ERROR_INVALID_SERVICE_CONTROL;
119 else
120 dwError = RtlNtStatusToDosError(Status);
121
122 /* Release driver-unloading privilege */
123 RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE,
124 WasPrivilegeEnabled,
125 FALSE,
126 &WasPrivilegeEnabled);
127
128done:
129 HeapFree(GetProcessHeap(), 0, pszDriverPath);
130 return dwError;
131}
132
133
134static
135DWORD
136ScmGetDriverStatus(PSERVICE lpService,
137 LPSERVICE_STATUS lpServiceStatus)
138{
139 OBJECT_ATTRIBUTES ObjectAttributes;
140 UNICODE_STRING DirName;
141 HANDLE DirHandle;
142 NTSTATUS Status = STATUS_SUCCESS;
143 POBJECT_DIRECTORY_INFORMATION DirInfo;
144 ULONG BufferLength;
145 ULONG DataLength;
146 ULONG Index;
147 DWORD dwError = ERROR_SUCCESS;
148 BOOLEAN bFound = FALSE;
149 DWORD dwPreviousState;
150
151 DPRINT1("ScmGetDriverStatus() called\n");
152
153 /* Zero output buffer if any */
154 if (lpServiceStatus != NULL)
155 {
156 memset(lpServiceStatus, 0, sizeof(SERVICE_STATUS));
157 }
158
159 /* Select the appropriate object directory based on driver type */
160 if (lpService->Status.dwServiceType == SERVICE_KERNEL_DRIVER)
161 {
162 RtlInitUnicodeString(&DirName, L"\\Driver");
163 }
164 else // if (lpService->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER)
165 {
166 ASSERT(lpService->Status.dwServiceType == SERVICE_FILE_SYSTEM_DRIVER);
167 RtlInitUnicodeString(&DirName, L"\\FileSystem");
168 }
169
170 InitializeObjectAttributes(&ObjectAttributes,
171 &DirName,
172 0,
173 NULL,
174 NULL);
175
176 /* Open the object directory where loaded drivers are */
177 Status = NtOpenDirectoryObject(&DirHandle,
178 DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
179 &ObjectAttributes);
180 if (!NT_SUCCESS(Status))
181 {
182 DPRINT1("NtOpenDirectoryObject() failed\n");
183 return RtlNtStatusToDosError(Status);
184 }
185
186 /* Allocate a buffer big enough for querying the object */
187 BufferLength = sizeof(OBJECT_DIRECTORY_INFORMATION) +
188 2 * MAX_PATH * sizeof(WCHAR);
189 DirInfo = (OBJECT_DIRECTORY_INFORMATION*) HeapAlloc(GetProcessHeap(),
190 HEAP_ZERO_MEMORY,
191 BufferLength);
192
193 /* Now, start browsing entry by entry */
194 Index = 0;
195 while (TRUE)
196 {
197 Status = NtQueryDirectoryObject(DirHandle,
198 DirInfo,
199 BufferLength,
200 TRUE,
201 FALSE,
202 &Index,
203 &DataLength);
204 /* End of enumeration, the driver was not found */
205 if (Status == STATUS_NO_MORE_ENTRIES)
206 {
207 DPRINT("No more services\n");
208 break;
209 }
210
211 /* Other error, fail */
212 if (!NT_SUCCESS(Status))
213 break;
214
215 DPRINT("Comparing: '%S' '%wZ'\n", lpService->lpServiceName, &DirInfo->Name);
216
217 /* Compare names to check whether it matches our driver */
218 if (_wcsicmp(lpService->lpServiceName, DirInfo->Name.Buffer) == 0)
219 {
220 /* That's our driver, bail out! */
221 DPRINT1("Found: '%S' '%wZ'\n",
222 lpService->lpServiceName, &DirInfo->Name);
223 bFound = TRUE;
224
225 break;
226 }
227 }
228
229 /* Release resources we don't need */
230 HeapFree(GetProcessHeap(),
231 0,
232 DirInfo);
233 NtClose(DirHandle);
234
235 /* Only quit if there's a failure
236 * Not having found the driver is legit!
237 * It means the driver was registered as a service, but not loaded
238 * We have not to fail in that situation, but to return proper status
239 */
240 if (!NT_SUCCESS(Status) && Status != STATUS_NO_MORE_ENTRIES)
241 {
242 DPRINT1("Status: %lx\n", Status);
243 return RtlNtStatusToDosError(Status);
244 }
245
246 /* Now, we have two cases:
247 * We found the driver: it means it's running
248 * We didn't find the driver: it wasn't running
249 */
250 if (bFound)
251 {
252 /* Found, return it's running */
253
254 dwPreviousState = lpService->Status.dwCurrentState;
255
256 /* It is running */
257 lpService->Status.dwCurrentState = SERVICE_RUNNING;
258
259 if (dwPreviousState == SERVICE_STOPPED)
260 {
261 /* Make it run if it was stopped before */
262 lpService->Status.dwWin32ExitCode = ERROR_SUCCESS;
263 lpService->Status.dwServiceSpecificExitCode = ERROR_SUCCESS;
264 lpService->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
265 lpService->Status.dwCheckPoint = 0;
266 lpService->Status.dwWaitHint = 0;
267 }
268
269 if (lpService->Status.dwWin32ExitCode == ERROR_SERVICE_NEVER_STARTED)
270 lpService->Status.dwWin32ExitCode = ERROR_SUCCESS;
271 }
272 else
273 {
274 /* Not found, return it's stopped */
275
276 if (lpService->Status.dwCurrentState == SERVICE_STOP_PENDING)
277 {
278 /* Stopped successfully */
279 lpService->Status.dwWin32ExitCode = ERROR_SUCCESS;
280 lpService->Status.dwCurrentState = SERVICE_STOPPED;
281 lpService->Status.dwControlsAccepted = 0;
282 lpService->Status.dwCheckPoint = 0;
283 lpService->Status.dwWaitHint = 0;
284 }
285 else if (lpService->Status.dwCurrentState == SERVICE_STOPPED)
286 {
287 /* Don't change the current status */
288 }
289 else
290 {
291 lpService->Status.dwWin32ExitCode = ERROR_GEN_FAILURE;
292 lpService->Status.dwCurrentState = SERVICE_STOPPED;
293 lpService->Status.dwControlsAccepted = 0;
294 lpService->Status.dwCheckPoint = 0;
295 lpService->Status.dwWaitHint = 0;
296 }
297 }
298
299 /* Copy service status if required */
300 if (lpServiceStatus != NULL)
301 {
302 RtlCopyMemory(lpServiceStatus,
303 &lpService->Status,
304 sizeof(SERVICE_STATUS));
305 }
306
307 DPRINT1("ScmGetDriverStatus() done (Error: %lu)\n", dwError);
308
309 return ERROR_SUCCESS;
310}
311
312
313DWORD
314ScmStartDriver(PSERVICE pService)
315{
316 DWORD dwError;
317
318 DPRINT("ScmStartDriver(%p)\n", pService);
319
320 dwError = ScmLoadDriver(pService);
321 if (dwError == ERROR_SUCCESS)
322 {
323 pService->Status.dwCurrentState = SERVICE_RUNNING;
324 pService->Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
325 pService->Status.dwWin32ExitCode = ERROR_SUCCESS;
326 }
327
328 DPRINT("ScmStartDriver returns %lu\n", dwError);
329
330 return dwError;
331}
332
333
334DWORD
335ScmControlDriver(PSERVICE lpService,
336 DWORD dwControl,
337 LPSERVICE_STATUS lpServiceStatus)
338{
339 DWORD dwError;
340
341 DPRINT("ScmControlDriver() called\n");
342
343 switch (dwControl)
344 {
345 case SERVICE_CONTROL_STOP:
346 /* Check the drivers status */
347 dwError = ScmGetDriverStatus(lpService,
348 lpServiceStatus);
349 if (dwError != ERROR_SUCCESS)
350 goto done;
351
352 /* Fail, if it is not running */
353 if (lpService->Status.dwCurrentState != SERVICE_RUNNING)
354 {
355 dwError = ERROR_INVALID_SERVICE_CONTROL;
356 goto done;
357 }
358
359 /* Unload the driver */
360 dwError = ScmUnloadDriver(lpService);
361 if (dwError == ERROR_INVALID_SERVICE_CONTROL)
362 {
363 /* The driver cannot be stopped, mark it non-stoppable */
364 lpService->Status.dwControlsAccepted = 0;
365 goto done;
366 }
367
368 /* Make the driver 'stop pending' */
369 lpService->Status.dwCurrentState = SERVICE_STOP_PENDING;
370
371 /* Check the drivers status again */
372 dwError = ScmGetDriverStatus(lpService,
373 lpServiceStatus);
374 break;
375
376 case SERVICE_CONTROL_INTERROGATE:
377 dwError = ScmGetDriverStatus(lpService,
378 lpServiceStatus);
379 break;
380
381 default:
382 dwError = ERROR_INVALID_SERVICE_CONTROL;
383 }
384
385done:
386 DPRINT("ScmControlDriver() done (Erorr: %lu)\n", dwError);
387
388 return dwError;
389}
390
391/* EOF */