xref: /reactos/base/services/umpnpmgr/umpnpmgr.c (revision bbccad0e)
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(
209     _In_ DWORD dwState,
210     _In_ DWORD dwCheckPoint)
211 {
212     ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
213     ServiceStatus.dwCurrentState = dwState;
214     ServiceStatus.dwWin32ExitCode = 0;
215     ServiceStatus.dwServiceSpecificExitCode = 0;
216     ServiceStatus.dwCheckPoint = dwCheckPoint;
217 
218     if (dwState == SERVICE_RUNNING)
219         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
220     else
221         ServiceStatus.dwControlsAccepted = 0;
222 
223     if (dwState == SERVICE_START_PENDING ||
224         dwState == SERVICE_STOP_PENDING ||
225         dwState == SERVICE_PAUSE_PENDING ||
226         dwState == SERVICE_CONTINUE_PENDING)
227         ServiceStatus.dwWaitHint = 10000;
228     else
229         ServiceStatus.dwWaitHint = 0;
230 
231     SetServiceStatus(ServiceStatusHandle,
232                      &ServiceStatus);
233 }
234 
235 
236 static DWORD WINAPI
237 ServiceControlHandler(DWORD dwControl,
238                       DWORD dwEventType,
239                       LPVOID lpEventData,
240                       LPVOID lpContext)
241 {
242     DPRINT1("ServiceControlHandler() called\n");
243 
244     switch (dwControl)
245     {
246         case SERVICE_CONTROL_STOP:
247             DPRINT1("  SERVICE_CONTROL_STOP received\n");
248             UpdateServiceStatus(SERVICE_STOP_PENDING, 1);
249             /* Stop listening to RPC Messages */
250             RpcMgmtStopServerListening(NULL);
251             UpdateServiceStatus(SERVICE_STOPPED, 0);
252             return ERROR_SUCCESS;
253 
254         case SERVICE_CONTROL_PAUSE:
255             DPRINT1("  SERVICE_CONTROL_PAUSE received\n");
256             UpdateServiceStatus(SERVICE_PAUSED, 0);
257             return ERROR_SUCCESS;
258 
259         case SERVICE_CONTROL_CONTINUE:
260             DPRINT1("  SERVICE_CONTROL_CONTINUE received\n");
261             UpdateServiceStatus(SERVICE_RUNNING, 0);
262             return ERROR_SUCCESS;
263 
264         case SERVICE_CONTROL_INTERROGATE:
265             DPRINT1("  SERVICE_CONTROL_INTERROGATE received\n");
266             SetServiceStatus(ServiceStatusHandle,
267                              &ServiceStatus);
268             return ERROR_SUCCESS;
269 
270         case SERVICE_CONTROL_SHUTDOWN:
271             DPRINT1("  SERVICE_CONTROL_SHUTDOWN received\n");
272             UpdateServiceStatus(SERVICE_STOP_PENDING, 1);
273             /* Stop listening to RPC Messages */
274             RpcMgmtStopServerListening(NULL);
275             UpdateServiceStatus(SERVICE_STOPPED, 0);
276             return ERROR_SUCCESS;
277 
278         default :
279             DPRINT1("  Control %lu received\n", dwControl);
280             return ERROR_CALL_NOT_IMPLEMENTED;
281     }
282 }
283 
284 static DWORD
285 GetBooleanRegValue(
286     IN HKEY hKey,
287     IN PCWSTR lpSubKey,
288     IN PCWSTR lpValue,
289     OUT PBOOL pValue)
290 {
291     DWORD dwError, dwType, dwData;
292     DWORD cbData = sizeof(dwData);
293     HKEY hSubKey = NULL;
294 
295     /* Default value */
296     *pValue = FALSE;
297 
298     dwError = RegOpenKeyExW(hKey,
299                             lpSubKey,
300                             0,
301                             KEY_READ,
302                             &hSubKey);
303     if (dwError != ERROR_SUCCESS)
304     {
305         DPRINT("GetBooleanRegValue(): RegOpenKeyExW() has failed to open '%S' key! (Error: %lu)\n",
306                lpSubKey, dwError);
307         return dwError;
308     }
309 
310     dwError = RegQueryValueExW(hSubKey,
311                                lpValue,
312                                0,
313                                &dwType,
314                                (PBYTE)&dwData,
315                                &cbData);
316     if (dwError != ERROR_SUCCESS)
317     {
318         DPRINT("GetBooleanRegValue(): RegQueryValueExW() has failed to query '%S' value! (Error: %lu)\n",
319                lpValue, dwError);
320         goto Cleanup;
321     }
322     if (dwType != REG_DWORD)
323     {
324         DPRINT("GetBooleanRegValue(): The value is not of REG_DWORD type!\n");
325         goto Cleanup;
326     }
327 
328     /* Return the value */
329     *pValue = (dwData == 1);
330 
331 Cleanup:
332     RegCloseKey(hSubKey);
333 
334     return dwError;
335 }
336 
337 BOOL
338 GetSuppressNewUIValue(VOID)
339 {
340     BOOL bSuppressNewHWUI = FALSE;
341 
342     /*
343      * Query the SuppressNewHWUI policy registry value. Don't cache it
344      * as we want to update our behaviour in consequence.
345      */
346     GetBooleanRegValue(HKEY_LOCAL_MACHINE,
347                        L"Software\\Policies\\Microsoft\\Windows\\DeviceInstall\\Settings",
348                        L"SuppressNewHWUI",
349                        &bSuppressNewHWUI);
350     if (bSuppressNewHWUI)
351         DPRINT("GetSuppressNewUIValue(): newdev.dll's wizard UI won't be shown!\n");
352 
353     return bSuppressNewHWUI;
354 }
355 
356 VOID WINAPI
357 ServiceMain(DWORD argc, LPTSTR *argv)
358 {
359     HANDLE hThread;
360     DWORD dwThreadId;
361 
362     UNREFERENCED_PARAMETER(argc);
363     UNREFERENCED_PARAMETER(argv);
364 
365     DPRINT("ServiceMain() called\n");
366 
367     ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName,
368                                                         ServiceControlHandler,
369                                                         NULL);
370     if (!ServiceStatusHandle)
371     {
372         DPRINT1("RegisterServiceCtrlHandlerExW() failed! (Error %lu)\n", GetLastError());
373         return;
374     }
375 
376     UpdateServiceStatus(SERVICE_START_PENDING, 1);
377 
378     hThread = CreateThread(NULL,
379                            0,
380                            PnpEventThread,
381                            NULL,
382                            0,
383                            &dwThreadId);
384     if (hThread != NULL)
385         CloseHandle(hThread);
386 
387     UpdateServiceStatus(SERVICE_START_PENDING, 2);
388 
389     hThread = CreateThread(NULL,
390                            0,
391                            RpcServerThread,
392                            NULL,
393                            0,
394                            &dwThreadId);
395     if (hThread != NULL)
396         CloseHandle(hThread);
397 
398     UpdateServiceStatus(SERVICE_START_PENDING, 3);
399 
400     hThread = CreateThread(NULL,
401                            0,
402                            DeviceInstallThread,
403                            NULL,
404                            0,
405                            &dwThreadId);
406     if (hThread != NULL)
407         CloseHandle(hThread);
408 
409     UpdateServiceStatus(SERVICE_RUNNING, 0);
410 
411     DPRINT("ServiceMain() done\n");
412 }
413 
414 static DWORD
415 InitializePnPManager(VOID)
416 {
417     BOOLEAN OldValue;
418     DWORD dwError;
419 
420     DPRINT("UMPNPMGR: InitializePnPManager() started\n");
421 
422     /* We need this privilege for using CreateProcessAsUserW */
423     RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &OldValue);
424 
425     hInstallEvent = CreateEventW(NULL, TRUE, SetupIsActive()/*FALSE*/, NULL);
426     if (hInstallEvent == NULL)
427     {
428         dwError = GetLastError();
429         DPRINT1("Could not create the Install Event! (Error %lu)\n", dwError);
430         return dwError;
431     }
432 
433     hNoPendingInstalls = CreateEventW(NULL,
434                                       TRUE,
435                                       FALSE,
436                                       L"Global\\PnP_No_Pending_Install_Events");
437     if (hNoPendingInstalls == NULL)
438     {
439         dwError = GetLastError();
440         DPRINT1("Could not create the Pending-Install Event! (Error %lu)\n", dwError);
441         return dwError;
442     }
443 
444     /*
445      * Initialize the device-install event list
446      */
447 
448     hDeviceInstallListNotEmpty = CreateEventW(NULL, FALSE, FALSE, NULL);
449     if (hDeviceInstallListNotEmpty == NULL)
450     {
451         dwError = GetLastError();
452         DPRINT1("Could not create the List Event! (Error %lu)\n", dwError);
453         return dwError;
454     }
455 
456     hDeviceInstallListMutex = CreateMutexW(NULL, FALSE, NULL);
457     if (hDeviceInstallListMutex == NULL)
458     {
459         dwError = GetLastError();
460         DPRINT1("Could not create the List Mutex! (Error %lu)\n", dwError);
461         return dwError;
462     }
463     InitializeListHead(&DeviceInstallListHead);
464 
465     /* Query the SuppressUI registry value and cache it for our whole lifetime */
466     GetBooleanRegValue(HKEY_LOCAL_MACHINE,
467                        L"System\\CurrentControlSet\\Services\\PlugPlay\\Parameters",
468                        L"SuppressUI",
469                        &g_IsUISuppressed);
470     if (g_IsUISuppressed)
471         DPRINT("UMPNPMGR: newdev.dll's wizard UI won't be shown!\n");
472 
473     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
474                             L"System\\CurrentControlSet\\Enum",
475                             0,
476                             KEY_ALL_ACCESS,
477                             &hEnumKey);
478     if (dwError != ERROR_SUCCESS)
479     {
480         DPRINT1("Could not open the Enum Key! (Error %lu)\n", dwError);
481         return dwError;
482     }
483 
484     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
485                             L"System\\CurrentControlSet\\Control\\Class",
486                             0,
487                             KEY_ALL_ACCESS,
488                             &hClassKey);
489     if (dwError != ERROR_SUCCESS)
490     {
491         DPRINT1("Could not open the Class Key! (Error %lu)\n", dwError);
492         return dwError;
493     }
494 
495     DPRINT("UMPNPMGR: InitializePnPManager() done\n");
496 
497     return 0;
498 }
499 
500 BOOL WINAPI
501 DllMain(HINSTANCE hinstDLL,
502         DWORD fdwReason,
503         LPVOID lpvReserved)
504 {
505     switch (fdwReason)
506     {
507         case DLL_PROCESS_ATTACH:
508             DisableThreadLibraryCalls(hinstDLL);
509             InitializePnPManager();
510             break;
511 
512         case DLL_PROCESS_DETACH:
513             break;
514     }
515 
516     return TRUE;
517 }
518 
519 /* EOF */
520