xref: /reactos/base/services/umpnpmgr/umpnpmgr.c (revision b5218987)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2005 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * COPYRIGHT:        See COPYING in the top level directory
21  * PROJECT:          ReactOS kernel
22  * FILE:             base/services/umpnpmgr/umpnpmgr.c
23  * PURPOSE:          User-mode Plug and Play manager
24  * PROGRAMMER:       Eric Kohl (eric.kohl@reactos.org)
25  *                   Herv� Poussineau (hpoussin@reactos.org)
26  *                   Colin Finck (colin@reactos.org)
27  */
28 
29 /* INCLUDES *****************************************************************/
30 
31 #include "precomp.h"
32 
33 #define NDEBUG
34 #include <debug.h>
35 
36 
37 /* GLOBALS ******************************************************************/
38 
39 static WCHAR ServiceName[] = L"PlugPlay";
40 
41 static SERVICE_STATUS_HANDLE ServiceStatusHandle;
42 static SERVICE_STATUS ServiceStatus;
43 
44 HKEY hEnumKey = NULL;
45 HKEY hClassKey = NULL;
46 BOOL g_IsUISuppressed = FALSE;
47 
48 /* FUNCTIONS *****************************************************************/
49 
50 static DWORD WINAPI
51 PnpEventThread(LPVOID lpParameter)
52 {
53     PLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData = {0, 0, 0, 0};
54     DWORD dwRet = ERROR_SUCCESS;
55     NTSTATUS Status;
56     RPC_STATUS RpcStatus;
57     PPLUGPLAY_EVENT_BLOCK PnpEvent, NewPnpEvent;
58     ULONG PnpEventSize;
59 
60     UNREFERENCED_PARAMETER(lpParameter);
61 
62     PnpEventSize = 0x1000;
63     PnpEvent = HeapAlloc(GetProcessHeap(), 0, PnpEventSize);
64     if (PnpEvent == NULL)
65         return ERROR_OUTOFMEMORY;
66 
67     for (;;)
68     {
69         DPRINT("Calling NtGetPlugPlayEvent()\n");
70 
71         /* Wait for the next PnP event */
72         Status = NtGetPlugPlayEvent(0, 0, PnpEvent, PnpEventSize);
73 
74         /* Resize the buffer for the PnP event if it's too small */
75         if (Status == STATUS_BUFFER_TOO_SMALL)
76         {
77             PnpEventSize += 0x400;
78             NewPnpEvent = HeapReAlloc(GetProcessHeap(), 0, PnpEvent, PnpEventSize);
79             if (NewPnpEvent == NULL)
80             {
81                 dwRet = ERROR_OUTOFMEMORY;
82                 break;
83             }
84             PnpEvent = NewPnpEvent;
85             continue;
86         }
87 
88         if (!NT_SUCCESS(Status))
89         {
90             DPRINT1("NtGetPlugPlayEvent() failed (Status 0x%08lx)\n", Status);
91             break;
92         }
93 
94         /* Process the PnP event */
95         DPRINT("Received PnP Event\n");
96         if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_ENUMERATED, &RpcStatus))
97         {
98             DeviceInstallParams* Params;
99             DWORD len;
100             DWORD DeviceIdLength;
101 
102             DPRINT("Device enumerated: %S\n", PnpEvent->TargetDevice.DeviceIds);
103 
104             DeviceIdLength = lstrlenW(PnpEvent->TargetDevice.DeviceIds);
105             if (DeviceIdLength)
106             {
107                 /* Queue device install (will be dequeued by DeviceInstallThread) */
108                 len = FIELD_OFFSET(DeviceInstallParams, DeviceIds) + (DeviceIdLength + 1) * sizeof(WCHAR);
109                 Params = HeapAlloc(GetProcessHeap(), 0, len);
110                 if (Params)
111                 {
112                     wcscpy(Params->DeviceIds, PnpEvent->TargetDevice.DeviceIds);
113                     InterlockedPushEntrySList(&DeviceInstallListHead, &Params->ListEntry);
114                     SetEvent(hDeviceInstallListNotEmpty);
115                 }
116             }
117         }
118         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_ARRIVAL, &RpcStatus))
119         {
120 //            DWORD dwRecipient;
121 
122             DPRINT("Device arrival: %S\n", PnpEvent->TargetDevice.DeviceIds);
123 
124 //            dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
125 //            BroadcastSystemMessageW(BSF_POSTMESSAGE,
126 //                                    &dwRecipient,
127 //                                    WM_DEVICECHANGE,
128 //                                    DBT_DEVNODES_CHANGED,
129 //                                    0);
130             SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
131         }
132         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_EJECT_VETOED, &RpcStatus))
133         {
134             DPRINT1("Eject vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds);
135         }
136         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_KERNEL_INITIATED_EJECT, &RpcStatus))
137         {
138             DPRINT1("Kernel initiated eject: %S\n", PnpEvent->TargetDevice.DeviceIds);
139         }
140         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SAFE_REMOVAL, &RpcStatus))
141         {
142 //            DWORD dwRecipient;
143 
144             DPRINT1("Safe removal: %S\n", PnpEvent->TargetDevice.DeviceIds);
145 
146 //            dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
147 //            BroadcastSystemMessageW(BSF_POSTMESSAGE,
148 //                                    &dwRecipient,
149 //                                    WM_DEVICECHANGE,
150 //                                    DBT_DEVNODES_CHANGED,
151 //                                    0);
152             SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
153         }
154         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SURPRISE_REMOVAL, &RpcStatus))
155         {
156 //            DWORD dwRecipient;
157 
158             DPRINT1("Surprise removal: %S\n", PnpEvent->TargetDevice.DeviceIds);
159 
160 //            dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
161 //            BroadcastSystemMessageW(BSF_POSTMESSAGE,
162 //                                    &dwRecipient,
163 //                                    WM_DEVICECHANGE,
164 //                                    DBT_DEVNODES_CHANGED,
165 //                                    0);
166             SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
167         }
168         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVAL_VETOED, &RpcStatus))
169         {
170             DPRINT1("Removal vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds);
171         }
172         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVE_PENDING, &RpcStatus))
173         {
174             DPRINT1("Removal pending: %S\n", PnpEvent->TargetDevice.DeviceIds);
175         }
176         else
177         {
178             DPRINT1("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
179                 PnpEvent->EventGuid.Data1, PnpEvent->EventGuid.Data2, PnpEvent->EventGuid.Data3,
180                 PnpEvent->EventGuid.Data4[0], PnpEvent->EventGuid.Data4[1], PnpEvent->EventGuid.Data4[2],
181                 PnpEvent->EventGuid.Data4[3], PnpEvent->EventGuid.Data4[4], PnpEvent->EventGuid.Data4[5],
182                 PnpEvent->EventGuid.Data4[6], PnpEvent->EventGuid.Data4[7]);
183         }
184 
185         /* Dequeue the current PnP event and signal the next one */
186         Status = NtPlugPlayControl(PlugPlayControlUserResponse,
187                                    &ResponseData,
188                                    sizeof(ResponseData));
189         if (!NT_SUCCESS(Status))
190         {
191             DPRINT1("NtPlugPlayControl(PlugPlayControlUserResponse) failed (Status 0x%08lx)\n", Status);
192             break;
193         }
194     }
195 
196     HeapFree(GetProcessHeap(), 0, PnpEvent);
197 
198     return dwRet;
199 }
200 
201 
202 static VOID
203 UpdateServiceStatus(DWORD dwState)
204 {
205     ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
206     ServiceStatus.dwCurrentState = dwState;
207     ServiceStatus.dwControlsAccepted = 0;
208     ServiceStatus.dwWin32ExitCode = 0;
209     ServiceStatus.dwServiceSpecificExitCode = 0;
210     ServiceStatus.dwCheckPoint = 0;
211 
212     if (dwState == SERVICE_START_PENDING ||
213         dwState == SERVICE_STOP_PENDING ||
214         dwState == SERVICE_PAUSE_PENDING ||
215         dwState == SERVICE_CONTINUE_PENDING)
216         ServiceStatus.dwWaitHint = 10000;
217     else
218         ServiceStatus.dwWaitHint = 0;
219 
220     SetServiceStatus(ServiceStatusHandle,
221                      &ServiceStatus);
222 }
223 
224 
225 static DWORD WINAPI
226 ServiceControlHandler(DWORD dwControl,
227                       DWORD dwEventType,
228                       LPVOID lpEventData,
229                       LPVOID lpContext)
230 {
231     DPRINT1("ServiceControlHandler() called\n");
232 
233     switch (dwControl)
234     {
235         case SERVICE_CONTROL_STOP:
236             DPRINT1("  SERVICE_CONTROL_STOP received\n");
237             /* Stop listening to RPC Messages */
238             RpcMgmtStopServerListening(NULL);
239             UpdateServiceStatus(SERVICE_STOPPED);
240             return ERROR_SUCCESS;
241 
242         case SERVICE_CONTROL_PAUSE:
243             DPRINT1("  SERVICE_CONTROL_PAUSE received\n");
244             UpdateServiceStatus(SERVICE_PAUSED);
245             return ERROR_SUCCESS;
246 
247         case SERVICE_CONTROL_CONTINUE:
248             DPRINT1("  SERVICE_CONTROL_CONTINUE received\n");
249             UpdateServiceStatus(SERVICE_RUNNING);
250             return ERROR_SUCCESS;
251 
252         case SERVICE_CONTROL_INTERROGATE:
253             DPRINT1("  SERVICE_CONTROL_INTERROGATE received\n");
254             SetServiceStatus(ServiceStatusHandle,
255                              &ServiceStatus);
256             return ERROR_SUCCESS;
257 
258         case SERVICE_CONTROL_SHUTDOWN:
259             DPRINT1("  SERVICE_CONTROL_SHUTDOWN received\n");
260             /* Stop listening to RPC Messages */
261             RpcMgmtStopServerListening(NULL);
262             UpdateServiceStatus(SERVICE_STOPPED);
263             return ERROR_SUCCESS;
264 
265         default :
266             DPRINT1("  Control %lu received\n", dwControl);
267             return ERROR_CALL_NOT_IMPLEMENTED;
268     }
269 }
270 
271 static DWORD
272 GetBooleanRegValue(
273     IN HKEY hKey,
274     IN PCWSTR lpSubKey,
275     IN PCWSTR lpValue,
276     OUT PBOOL pValue)
277 {
278     DWORD dwError, dwType, dwData;
279     DWORD cbData = sizeof(dwData);
280     HKEY hSubKey = NULL;
281 
282     /* Default value */
283     *pValue = FALSE;
284 
285     dwError = RegOpenKeyExW(hKey,
286                             lpSubKey,
287                             0,
288                             KEY_READ,
289                             &hSubKey);
290     if (dwError != ERROR_SUCCESS)
291     {
292         DPRINT("GetBooleanRegValue(): RegOpenKeyExW() has failed to open '%S' key! (Error: %lu)\n",
293                lpSubKey, dwError);
294         return dwError;
295     }
296 
297     dwError = RegQueryValueExW(hSubKey,
298                                lpValue,
299                                0,
300                                &dwType,
301                                (PBYTE)&dwData,
302                                &cbData);
303     if (dwError != ERROR_SUCCESS)
304     {
305         DPRINT("GetBooleanRegValue(): RegQueryValueExW() has failed to query '%S' value! (Error: %lu)\n",
306                lpValue, dwError);
307         goto Cleanup;
308     }
309     if (dwType != REG_DWORD)
310     {
311         DPRINT("GetBooleanRegValue(): The value is not of REG_DWORD type!\n");
312         goto Cleanup;
313     }
314 
315     /* Return the value */
316     *pValue = (dwData == 1);
317 
318 Cleanup:
319     RegCloseKey(hSubKey);
320 
321     return dwError;
322 }
323 
324 BOOL
325 GetSuppressNewUIValue(VOID)
326 {
327     BOOL bSuppressNewHWUI = FALSE;
328 
329     /*
330      * Query the SuppressNewHWUI policy registry value. Don't cache it
331      * as we want to update our behaviour in consequence.
332      */
333     GetBooleanRegValue(HKEY_LOCAL_MACHINE,
334                        L"Software\\Policies\\Microsoft\\Windows\\DeviceInstall\\Settings",
335                        L"SuppressNewHWUI",
336                        &bSuppressNewHWUI);
337     if (bSuppressNewHWUI)
338         DPRINT("GetSuppressNewUIValue(): newdev.dll's wizard UI won't be shown!\n");
339 
340     return bSuppressNewHWUI;
341 }
342 
343 VOID WINAPI
344 ServiceMain(DWORD argc, LPTSTR *argv)
345 {
346     HANDLE hThread;
347     DWORD dwThreadId;
348 
349     UNREFERENCED_PARAMETER(argc);
350     UNREFERENCED_PARAMETER(argv);
351 
352     DPRINT("ServiceMain() called\n");
353 
354     ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName,
355                                                         ServiceControlHandler,
356                                                         NULL);
357     if (!ServiceStatusHandle)
358     {
359         DPRINT1("RegisterServiceCtrlHandlerExW() failed! (Error %lu)\n", GetLastError());
360         return;
361     }
362 
363     UpdateServiceStatus(SERVICE_START_PENDING);
364 
365     hThread = CreateThread(NULL,
366                            0,
367                            PnpEventThread,
368                            NULL,
369                            0,
370                            &dwThreadId);
371     if (hThread != NULL)
372         CloseHandle(hThread);
373 
374     hThread = CreateThread(NULL,
375                            0,
376                            RpcServerThread,
377                            NULL,
378                            0,
379                            &dwThreadId);
380     if (hThread != NULL)
381         CloseHandle(hThread);
382 
383     hThread = CreateThread(NULL,
384                            0,
385                            DeviceInstallThread,
386                            NULL,
387                            0,
388                            &dwThreadId);
389     if (hThread != NULL)
390         CloseHandle(hThread);
391 
392     UpdateServiceStatus(SERVICE_RUNNING);
393 
394     DPRINT("ServiceMain() done\n");
395 }
396 
397 static DWORD
398 InitializePnPManager(VOID)
399 {
400     BOOLEAN OldValue;
401     DWORD dwError;
402 
403     DPRINT("UMPNPMGR: InitializePnPManager() started\n");
404 
405     /* We need this privilege for using CreateProcessAsUserW */
406     RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &OldValue);
407 
408     hInstallEvent = CreateEventW(NULL, TRUE, SetupIsActive()/*FALSE*/, NULL);
409     if (hInstallEvent == NULL)
410     {
411         dwError = GetLastError();
412         DPRINT1("Could not create the Install Event! (Error %lu)\n", dwError);
413         return dwError;
414     }
415 
416     hDeviceInstallListNotEmpty = CreateEventW(NULL, FALSE, FALSE, NULL);
417     if (hDeviceInstallListNotEmpty == NULL)
418     {
419         dwError = GetLastError();
420         DPRINT1("Could not create the Event! (Error %lu)\n", dwError);
421         return dwError;
422     }
423 
424     hNoPendingInstalls = CreateEventW(NULL,
425                                       TRUE,
426                                       FALSE,
427                                       L"Global\\PnP_No_Pending_Install_Events");
428     if (hNoPendingInstalls == NULL)
429     {
430         dwError = GetLastError();
431         DPRINT1("Could not create the Event! (Error %lu)\n", dwError);
432         return dwError;
433     }
434 
435     InitializeSListHead(&DeviceInstallListHead);
436 
437     /* Query the SuppressUI registry value and cache it for our whole lifetime */
438     GetBooleanRegValue(HKEY_LOCAL_MACHINE,
439                        L"System\\CurrentControlSet\\Services\\PlugPlay\\Parameters",
440                        L"SuppressUI",
441                        &g_IsUISuppressed);
442     if (g_IsUISuppressed)
443         DPRINT("UMPNPMGR: newdev.dll's wizard UI won't be shown!\n");
444 
445     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
446                             L"System\\CurrentControlSet\\Enum",
447                             0,
448                             KEY_ALL_ACCESS,
449                             &hEnumKey);
450     if (dwError != ERROR_SUCCESS)
451     {
452         DPRINT1("Could not open the Enum Key! (Error %lu)\n", dwError);
453         return dwError;
454     }
455 
456     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
457                             L"System\\CurrentControlSet\\Control\\Class",
458                             0,
459                             KEY_ALL_ACCESS,
460                             &hClassKey);
461     if (dwError != ERROR_SUCCESS)
462     {
463         DPRINT1("Could not open the Class Key! (Error %lu)\n", dwError);
464         return dwError;
465     }
466 
467     DPRINT("UMPNPMGR: InitializePnPManager() done\n");
468 
469     return 0;
470 }
471 
472 BOOL WINAPI
473 DllMain(HINSTANCE hinstDLL,
474         DWORD fdwReason,
475         LPVOID lpvReserved)
476 {
477     switch (fdwReason)
478     {
479         case DLL_PROCESS_ATTACH:
480             DisableThreadLibraryCalls(hinstDLL);
481             InitializePnPManager();
482             break;
483 
484         case DLL_PROCESS_DETACH:
485             break;
486     }
487 
488     return TRUE;
489 }
490 
491 /* EOF */
492