xref: /reactos/base/system/services/driver.c (revision 58588b76)
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 
22 static
23 DWORD
24 ScmLoadDriver(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 
69 done:
70     HeapFree(GetProcessHeap(), 0, pszDriverPath);
71     return RtlNtStatusToDosError(Status);
72 }
73 
74 
75 static
76 DWORD
77 ScmUnloadDriver(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 
128 done:
129     HeapFree(GetProcessHeap(), 0, pszDriverPath);
130     return dwError;
131 }
132 
133 
134 static
135 DWORD
136 ScmGetDriverStatus(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 
313 DWORD
314 ScmStartDriver(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 
334 DWORD
335 ScmControlDriver(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 
385 done:
386     DPRINT("ScmControlDriver() done (Erorr: %lu)\n", dwError);
387 
388     return dwError;
389 }
390 
391 /* EOF */
392