xref: /reactos/base/services/umpnpmgr/umpnpmgr.c (revision 40462c92)
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                 /* Allocate a new device-install event */
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 
114                     /* Queue the event (will be dequeued by DeviceInstallThread) */
115                     WaitForSingleObject(hDeviceInstallListMutex, INFINITE);
116                     InsertTailList(&DeviceInstallListHead, &Params->ListEntry);
117                     ReleaseMutex(hDeviceInstallListMutex);
118 
119                     SetEvent(hDeviceInstallListNotEmpty);
120                 }
121             }
122         }
123         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_ARRIVAL, &RpcStatus))
124         {
125 //            DWORD dwRecipient;
126 
127             DPRINT("Device arrival: %S\n", PnpEvent->TargetDevice.DeviceIds);
128 
129 //            dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
130 //            BroadcastSystemMessageW(BSF_POSTMESSAGE,
131 //                                    &dwRecipient,
132 //                                    WM_DEVICECHANGE,
133 //                                    DBT_DEVNODES_CHANGED,
134 //                                    0);
135             SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
136         }
137         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_EJECT_VETOED, &RpcStatus))
138         {
139             DPRINT1("Eject vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds);
140         }
141         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_KERNEL_INITIATED_EJECT, &RpcStatus))
142         {
143             DPRINT1("Kernel initiated eject: %S\n", PnpEvent->TargetDevice.DeviceIds);
144         }
145         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SAFE_REMOVAL, &RpcStatus))
146         {
147 //            DWORD dwRecipient;
148 
149             DPRINT1("Safe removal: %S\n", PnpEvent->TargetDevice.DeviceIds);
150 
151 //            dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
152 //            BroadcastSystemMessageW(BSF_POSTMESSAGE,
153 //                                    &dwRecipient,
154 //                                    WM_DEVICECHANGE,
155 //                                    DBT_DEVNODES_CHANGED,
156 //                                    0);
157             SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
158         }
159         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SURPRISE_REMOVAL, &RpcStatus))
160         {
161 //            DWORD dwRecipient;
162 
163             DPRINT1("Surprise removal: %S\n", PnpEvent->TargetDevice.DeviceIds);
164 
165 //            dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
166 //            BroadcastSystemMessageW(BSF_POSTMESSAGE,
167 //                                    &dwRecipient,
168 //                                    WM_DEVICECHANGE,
169 //                                    DBT_DEVNODES_CHANGED,
170 //                                    0);
171             SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
172         }
173         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVAL_VETOED, &RpcStatus))
174         {
175             DPRINT1("Removal vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds);
176         }
177         else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVE_PENDING, &RpcStatus))
178         {
179             DPRINT1("Removal pending: %S\n", PnpEvent->TargetDevice.DeviceIds);
180         }
181         else
182         {
183             DPRINT1("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
184                 PnpEvent->EventGuid.Data1, PnpEvent->EventGuid.Data2, PnpEvent->EventGuid.Data3,
185                 PnpEvent->EventGuid.Data4[0], PnpEvent->EventGuid.Data4[1], PnpEvent->EventGuid.Data4[2],
186                 PnpEvent->EventGuid.Data4[3], PnpEvent->EventGuid.Data4[4], PnpEvent->EventGuid.Data4[5],
187                 PnpEvent->EventGuid.Data4[6], PnpEvent->EventGuid.Data4[7]);
188         }
189 
190         /* Dequeue the current PnP event and signal the next one */
191         Status = NtPlugPlayControl(PlugPlayControlUserResponse,
192                                    &ResponseData,
193                                    sizeof(ResponseData));
194         if (!NT_SUCCESS(Status))
195         {
196             DPRINT1("NtPlugPlayControl(PlugPlayControlUserResponse) failed (Status 0x%08lx)\n", Status);
197             break;
198         }
199     }
200 
201     HeapFree(GetProcessHeap(), 0, PnpEvent);
202 
203     return dwRet;
204 }
205 
206 
207 static VOID
208 UpdateServiceStatus(DWORD dwState)
209 {
210     ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
211     ServiceStatus.dwCurrentState = dwState;
212     ServiceStatus.dwControlsAccepted = 0;
213     ServiceStatus.dwWin32ExitCode = 0;
214     ServiceStatus.dwServiceSpecificExitCode = 0;
215     ServiceStatus.dwCheckPoint = 0;
216 
217     if (dwState == SERVICE_START_PENDING ||
218         dwState == SERVICE_STOP_PENDING ||
219         dwState == SERVICE_PAUSE_PENDING ||
220         dwState == SERVICE_CONTINUE_PENDING)
221         ServiceStatus.dwWaitHint = 10000;
222     else
223         ServiceStatus.dwWaitHint = 0;
224 
225     SetServiceStatus(ServiceStatusHandle,
226                      &ServiceStatus);
227 }
228 
229 
230 static DWORD WINAPI
231 ServiceControlHandler(DWORD dwControl,
232                       DWORD dwEventType,
233                       LPVOID lpEventData,
234                       LPVOID lpContext)
235 {
236     DPRINT1("ServiceControlHandler() called\n");
237 
238     switch (dwControl)
239     {
240         case SERVICE_CONTROL_STOP:
241             DPRINT1("  SERVICE_CONTROL_STOP received\n");
242             /* Stop listening to RPC Messages */
243             RpcMgmtStopServerListening(NULL);
244             UpdateServiceStatus(SERVICE_STOPPED);
245             return ERROR_SUCCESS;
246 
247         case SERVICE_CONTROL_PAUSE:
248             DPRINT1("  SERVICE_CONTROL_PAUSE received\n");
249             UpdateServiceStatus(SERVICE_PAUSED);
250             return ERROR_SUCCESS;
251 
252         case SERVICE_CONTROL_CONTINUE:
253             DPRINT1("  SERVICE_CONTROL_CONTINUE received\n");
254             UpdateServiceStatus(SERVICE_RUNNING);
255             return ERROR_SUCCESS;
256 
257         case SERVICE_CONTROL_INTERROGATE:
258             DPRINT1("  SERVICE_CONTROL_INTERROGATE received\n");
259             SetServiceStatus(ServiceStatusHandle,
260                              &ServiceStatus);
261             return ERROR_SUCCESS;
262 
263         case SERVICE_CONTROL_SHUTDOWN:
264             DPRINT1("  SERVICE_CONTROL_SHUTDOWN received\n");
265             /* Stop listening to RPC Messages */
266             RpcMgmtStopServerListening(NULL);
267             UpdateServiceStatus(SERVICE_STOPPED);
268             return ERROR_SUCCESS;
269 
270         default :
271             DPRINT1("  Control %lu received\n", dwControl);
272             return ERROR_CALL_NOT_IMPLEMENTED;
273     }
274 }
275 
276 static DWORD
277 GetBooleanRegValue(
278     IN HKEY hKey,
279     IN PCWSTR lpSubKey,
280     IN PCWSTR lpValue,
281     OUT PBOOL pValue)
282 {
283     DWORD dwError, dwType, dwData;
284     DWORD cbData = sizeof(dwData);
285     HKEY hSubKey = NULL;
286 
287     /* Default value */
288     *pValue = FALSE;
289 
290     dwError = RegOpenKeyExW(hKey,
291                             lpSubKey,
292                             0,
293                             KEY_READ,
294                             &hSubKey);
295     if (dwError != ERROR_SUCCESS)
296     {
297         DPRINT("GetBooleanRegValue(): RegOpenKeyExW() has failed to open '%S' key! (Error: %lu)\n",
298                lpSubKey, dwError);
299         return dwError;
300     }
301 
302     dwError = RegQueryValueExW(hSubKey,
303                                lpValue,
304                                0,
305                                &dwType,
306                                (PBYTE)&dwData,
307                                &cbData);
308     if (dwError != ERROR_SUCCESS)
309     {
310         DPRINT("GetBooleanRegValue(): RegQueryValueExW() has failed to query '%S' value! (Error: %lu)\n",
311                lpValue, dwError);
312         goto Cleanup;
313     }
314     if (dwType != REG_DWORD)
315     {
316         DPRINT("GetBooleanRegValue(): The value is not of REG_DWORD type!\n");
317         goto Cleanup;
318     }
319 
320     /* Return the value */
321     *pValue = (dwData == 1);
322 
323 Cleanup:
324     RegCloseKey(hSubKey);
325 
326     return dwError;
327 }
328 
329 BOOL
330 GetSuppressNewUIValue(VOID)
331 {
332     BOOL bSuppressNewHWUI = FALSE;
333 
334     /*
335      * Query the SuppressNewHWUI policy registry value. Don't cache it
336      * as we want to update our behaviour in consequence.
337      */
338     GetBooleanRegValue(HKEY_LOCAL_MACHINE,
339                        L"Software\\Policies\\Microsoft\\Windows\\DeviceInstall\\Settings",
340                        L"SuppressNewHWUI",
341                        &bSuppressNewHWUI);
342     if (bSuppressNewHWUI)
343         DPRINT("GetSuppressNewUIValue(): newdev.dll's wizard UI won't be shown!\n");
344 
345     return bSuppressNewHWUI;
346 }
347 
348 VOID WINAPI
349 ServiceMain(DWORD argc, LPTSTR *argv)
350 {
351     HANDLE hThread;
352     DWORD dwThreadId;
353 
354     UNREFERENCED_PARAMETER(argc);
355     UNREFERENCED_PARAMETER(argv);
356 
357     DPRINT("ServiceMain() called\n");
358 
359     ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName,
360                                                         ServiceControlHandler,
361                                                         NULL);
362     if (!ServiceStatusHandle)
363     {
364         DPRINT1("RegisterServiceCtrlHandlerExW() failed! (Error %lu)\n", GetLastError());
365         return;
366     }
367 
368     UpdateServiceStatus(SERVICE_START_PENDING);
369 
370     hThread = CreateThread(NULL,
371                            0,
372                            PnpEventThread,
373                            NULL,
374                            0,
375                            &dwThreadId);
376     if (hThread != NULL)
377         CloseHandle(hThread);
378 
379     hThread = CreateThread(NULL,
380                            0,
381                            RpcServerThread,
382                            NULL,
383                            0,
384                            &dwThreadId);
385     if (hThread != NULL)
386         CloseHandle(hThread);
387 
388     hThread = CreateThread(NULL,
389                            0,
390                            DeviceInstallThread,
391                            NULL,
392                            0,
393                            &dwThreadId);
394     if (hThread != NULL)
395         CloseHandle(hThread);
396 
397     UpdateServiceStatus(SERVICE_RUNNING);
398 
399     DPRINT("ServiceMain() done\n");
400 }
401 
402 static DWORD
403 InitializePnPManager(VOID)
404 {
405     BOOLEAN OldValue;
406     DWORD dwError;
407 
408     DPRINT("UMPNPMGR: InitializePnPManager() started\n");
409 
410     /* We need this privilege for using CreateProcessAsUserW */
411     RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &OldValue);
412 
413     hInstallEvent = CreateEventW(NULL, TRUE, SetupIsActive()/*FALSE*/, NULL);
414     if (hInstallEvent == NULL)
415     {
416         dwError = GetLastError();
417         DPRINT1("Could not create the Install Event! (Error %lu)\n", dwError);
418         return dwError;
419     }
420 
421     hNoPendingInstalls = CreateEventW(NULL,
422                                       TRUE,
423                                       FALSE,
424                                       L"Global\\PnP_No_Pending_Install_Events");
425     if (hNoPendingInstalls == NULL)
426     {
427         dwError = GetLastError();
428         DPRINT1("Could not create the Pending-Install Event! (Error %lu)\n", dwError);
429         return dwError;
430     }
431 
432     /*
433      * Initialize the device-install event list
434      */
435 
436     hDeviceInstallListNotEmpty = CreateEventW(NULL, FALSE, FALSE, NULL);
437     if (hDeviceInstallListNotEmpty == NULL)
438     {
439         dwError = GetLastError();
440         DPRINT1("Could not create the List Event! (Error %lu)\n", dwError);
441         return dwError;
442     }
443 
444     hDeviceInstallListMutex = CreateMutexW(NULL, FALSE, NULL);
445     if (hDeviceInstallListMutex == NULL)
446     {
447         dwError = GetLastError();
448         DPRINT1("Could not create the List Mutex! (Error %lu)\n", dwError);
449         return dwError;
450     }
451     InitializeListHead(&DeviceInstallListHead);
452 
453     /* Query the SuppressUI registry value and cache it for our whole lifetime */
454     GetBooleanRegValue(HKEY_LOCAL_MACHINE,
455                        L"System\\CurrentControlSet\\Services\\PlugPlay\\Parameters",
456                        L"SuppressUI",
457                        &g_IsUISuppressed);
458     if (g_IsUISuppressed)
459         DPRINT("UMPNPMGR: newdev.dll's wizard UI won't be shown!\n");
460 
461     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
462                             L"System\\CurrentControlSet\\Enum",
463                             0,
464                             KEY_ALL_ACCESS,
465                             &hEnumKey);
466     if (dwError != ERROR_SUCCESS)
467     {
468         DPRINT1("Could not open the Enum Key! (Error %lu)\n", dwError);
469         return dwError;
470     }
471 
472     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
473                             L"System\\CurrentControlSet\\Control\\Class",
474                             0,
475                             KEY_ALL_ACCESS,
476                             &hClassKey);
477     if (dwError != ERROR_SUCCESS)
478     {
479         DPRINT1("Could not open the Class Key! (Error %lu)\n", dwError);
480         return dwError;
481     }
482 
483     DPRINT("UMPNPMGR: InitializePnPManager() done\n");
484 
485     return 0;
486 }
487 
488 BOOL WINAPI
489 DllMain(HINSTANCE hinstDLL,
490         DWORD fdwReason,
491         LPVOID lpvReserved)
492 {
493     switch (fdwReason)
494     {
495         case DLL_PROCESS_ATTACH:
496             DisableThreadLibraryCalls(hinstDLL);
497             InitializePnPManager();
498             break;
499 
500         case DLL_PROCESS_DETACH:
501             break;
502     }
503 
504     return TRUE;
505 }
506 
507 /* EOF */
508