[UMPNPMGR] Split the monolithic monster: service, rpc and installer.
[reactos.git] / base / services / umpnpmgr / umpnpmgr.c
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
47
48 /* FUNCTIONS *****************************************************************/
49
50 static DWORD WINAPI
51 PnpEventThread(LPVOID lpParameter)
52 {
53 DWORD dwRet = ERROR_SUCCESS;
54 NTSTATUS Status;
55 RPC_STATUS RpcStatus;
56 PPLUGPLAY_EVENT_BLOCK PnpEvent, NewPnpEvent;
57 ULONG PnpEventSize;
58
59 UNREFERENCED_PARAMETER(lpParameter);
60
61 PnpEventSize = 0x1000;
62 PnpEvent = HeapAlloc(GetProcessHeap(), 0, PnpEventSize);
63 if (PnpEvent == NULL)
64 return ERROR_OUTOFMEMORY;
65
66 for (;;)
67 {
68 DPRINT("Calling NtGetPlugPlayEvent()\n");
69
70 /* Wait for the next PnP event */
71 Status = NtGetPlugPlayEvent(0, 0, PnpEvent, PnpEventSize);
72
73 /* Resize the buffer for the PnP event if it's too small */
74 if (Status == STATUS_BUFFER_TOO_SMALL)
75 {
76 PnpEventSize += 0x400;
77 NewPnpEvent = HeapReAlloc(GetProcessHeap(), 0, PnpEvent, PnpEventSize);
78 if (NewPnpEvent == NULL)
79 {
80 dwRet = ERROR_OUTOFMEMORY;
81 break;
82 }
83 PnpEvent = NewPnpEvent;
84 continue;
85 }
86
87 if (!NT_SUCCESS(Status))
88 {
89 DPRINT1("NtGetPlugPlayEvent() failed (Status 0x%08lx)\n", Status);
90 break;
91 }
92
93 /* Process the PnP event */
94 DPRINT("Received PnP Event\n");
95 if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_ENUMERATED, &RpcStatus))
96 {
97 DeviceInstallParams* Params;
98 DWORD len;
99 DWORD DeviceIdLength;
100
101 DPRINT("Device enumerated: %S\n", PnpEvent->TargetDevice.DeviceIds);
102
103 DeviceIdLength = lstrlenW(PnpEvent->TargetDevice.DeviceIds);
104 if (DeviceIdLength)
105 {
106 /* Queue device install (will be dequeued by DeviceInstallThread) */
107 len = FIELD_OFFSET(DeviceInstallParams, DeviceIds) + (DeviceIdLength + 1) * sizeof(WCHAR);
108 Params = HeapAlloc(GetProcessHeap(), 0, len);
109 if (Params)
110 {
111 wcscpy(Params->DeviceIds, PnpEvent->TargetDevice.DeviceIds);
112 InterlockedPushEntrySList(&DeviceInstallListHead, &Params->ListEntry);
113 SetEvent(hDeviceInstallListNotEmpty);
114 }
115 }
116 }
117 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_ARRIVAL, &RpcStatus))
118 {
119 // DWORD dwRecipient;
120
121 DPRINT("Device arrival: %S\n", PnpEvent->TargetDevice.DeviceIds);
122
123 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
124 // BroadcastSystemMessageW(BSF_POSTMESSAGE,
125 // &dwRecipient,
126 // WM_DEVICECHANGE,
127 // DBT_DEVNODES_CHANGED,
128 // 0);
129 SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
130 }
131 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_EJECT_VETOED, &RpcStatus))
132 {
133 DPRINT1("Eject vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds);
134 }
135 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_KERNEL_INITIATED_EJECT, &RpcStatus))
136 {
137 DPRINT1("Kernel initiated eject: %S\n", PnpEvent->TargetDevice.DeviceIds);
138 }
139 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SAFE_REMOVAL, &RpcStatus))
140 {
141 // DWORD dwRecipient;
142
143 DPRINT1("Safe removal: %S\n", PnpEvent->TargetDevice.DeviceIds);
144
145 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
146 // BroadcastSystemMessageW(BSF_POSTMESSAGE,
147 // &dwRecipient,
148 // WM_DEVICECHANGE,
149 // DBT_DEVNODES_CHANGED,
150 // 0);
151 SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
152 }
153 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SURPRISE_REMOVAL, &RpcStatus))
154 {
155 // DWORD dwRecipient;
156
157 DPRINT1("Surprise removal: %S\n", PnpEvent->TargetDevice.DeviceIds);
158
159 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
160 // BroadcastSystemMessageW(BSF_POSTMESSAGE,
161 // &dwRecipient,
162 // WM_DEVICECHANGE,
163 // DBT_DEVNODES_CHANGED,
164 // 0);
165 SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
166 }
167 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVAL_VETOED, &RpcStatus))
168 {
169 DPRINT1("Removal vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds);
170 }
171 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVE_PENDING, &RpcStatus))
172 {
173 DPRINT1("Removal pending: %S\n", PnpEvent->TargetDevice.DeviceIds);
174 }
175 else
176 {
177 DPRINT1("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
178 PnpEvent->EventGuid.Data1, PnpEvent->EventGuid.Data2, PnpEvent->EventGuid.Data3,
179 PnpEvent->EventGuid.Data4[0], PnpEvent->EventGuid.Data4[1], PnpEvent->EventGuid.Data4[2],
180 PnpEvent->EventGuid.Data4[3], PnpEvent->EventGuid.Data4[4], PnpEvent->EventGuid.Data4[5],
181 PnpEvent->EventGuid.Data4[6], PnpEvent->EventGuid.Data4[7]);
182 }
183
184 /* Dequeue the current PnP event and signal the next one */
185 NtPlugPlayControl(PlugPlayControlUserResponse, NULL, 0);
186 }
187
188 HeapFree(GetProcessHeap(), 0, PnpEvent);
189
190 return dwRet;
191 }
192
193
194 static VOID
195 UpdateServiceStatus(DWORD dwState)
196 {
197 ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
198 ServiceStatus.dwCurrentState = dwState;
199 ServiceStatus.dwControlsAccepted = 0;
200 ServiceStatus.dwWin32ExitCode = 0;
201 ServiceStatus.dwServiceSpecificExitCode = 0;
202 ServiceStatus.dwCheckPoint = 0;
203
204 if (dwState == SERVICE_START_PENDING ||
205 dwState == SERVICE_STOP_PENDING ||
206 dwState == SERVICE_PAUSE_PENDING ||
207 dwState == SERVICE_CONTINUE_PENDING)
208 ServiceStatus.dwWaitHint = 10000;
209 else
210 ServiceStatus.dwWaitHint = 0;
211
212 SetServiceStatus(ServiceStatusHandle,
213 &ServiceStatus);
214 }
215
216
217 static DWORD WINAPI
218 ServiceControlHandler(DWORD dwControl,
219 DWORD dwEventType,
220 LPVOID lpEventData,
221 LPVOID lpContext)
222 {
223 DPRINT1("ServiceControlHandler() called\n");
224
225 switch (dwControl)
226 {
227 case SERVICE_CONTROL_STOP:
228 DPRINT1(" SERVICE_CONTROL_STOP received\n");
229 /* Stop listening to RPC Messages */
230 RpcMgmtStopServerListening(NULL);
231 UpdateServiceStatus(SERVICE_STOPPED);
232 return ERROR_SUCCESS;
233
234 case SERVICE_CONTROL_PAUSE:
235 DPRINT1(" SERVICE_CONTROL_PAUSE received\n");
236 UpdateServiceStatus(SERVICE_PAUSED);
237 return ERROR_SUCCESS;
238
239 case SERVICE_CONTROL_CONTINUE:
240 DPRINT1(" SERVICE_CONTROL_CONTINUE received\n");
241 UpdateServiceStatus(SERVICE_RUNNING);
242 return ERROR_SUCCESS;
243
244 case SERVICE_CONTROL_INTERROGATE:
245 DPRINT1(" SERVICE_CONTROL_INTERROGATE received\n");
246 SetServiceStatus(ServiceStatusHandle,
247 &ServiceStatus);
248 return ERROR_SUCCESS;
249
250 case SERVICE_CONTROL_SHUTDOWN:
251 DPRINT1(" SERVICE_CONTROL_SHUTDOWN received\n");
252 /* Stop listening to RPC Messages */
253 RpcMgmtStopServerListening(NULL);
254 UpdateServiceStatus(SERVICE_STOPPED);
255 return ERROR_SUCCESS;
256
257 default :
258 DPRINT1(" Control %lu received\n", dwControl);
259 return ERROR_CALL_NOT_IMPLEMENTED;
260 }
261 }
262
263
264 VOID WINAPI
265 ServiceMain(DWORD argc, LPTSTR *argv)
266 {
267 HANDLE hThread;
268 DWORD dwThreadId;
269
270 UNREFERENCED_PARAMETER(argc);
271 UNREFERENCED_PARAMETER(argv);
272
273 DPRINT("ServiceMain() called\n");
274
275 ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName,
276 ServiceControlHandler,
277 NULL);
278 if (!ServiceStatusHandle)
279 {
280 DPRINT1("RegisterServiceCtrlHandlerExW() failed! (Error %lu)\n", GetLastError());
281 return;
282 }
283
284 UpdateServiceStatus(SERVICE_START_PENDING);
285
286 hThread = CreateThread(NULL,
287 0,
288 PnpEventThread,
289 NULL,
290 0,
291 &dwThreadId);
292 if (hThread != NULL)
293 CloseHandle(hThread);
294
295 hThread = CreateThread(NULL,
296 0,
297 RpcServerThread,
298 NULL,
299 0,
300 &dwThreadId);
301 if (hThread != NULL)
302 CloseHandle(hThread);
303
304 hThread = CreateThread(NULL,
305 0,
306 DeviceInstallThread,
307 NULL,
308 0,
309 &dwThreadId);
310 if (hThread != NULL)
311 CloseHandle(hThread);
312
313 UpdateServiceStatus(SERVICE_RUNNING);
314
315 DPRINT("ServiceMain() done\n");
316 }
317
318 static DWORD
319 InitializePnPManager(VOID)
320 {
321 BOOLEAN OldValue;
322 DWORD dwError;
323
324 DPRINT("UMPNPMGR: InitializePnPManager() started\n");
325
326 /* We need this privilege for using CreateProcessAsUserW */
327 RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &OldValue);
328
329 hInstallEvent = CreateEventW(NULL, TRUE, SetupIsActive()/*FALSE*/, NULL);
330 if (hInstallEvent == NULL)
331 {
332 dwError = GetLastError();
333 DPRINT1("Could not create the Install Event! (Error %lu)\n", dwError);
334 return dwError;
335 }
336
337 hDeviceInstallListNotEmpty = CreateEventW(NULL, FALSE, FALSE, NULL);
338 if (hDeviceInstallListNotEmpty == NULL)
339 {
340 dwError = GetLastError();
341 DPRINT1("Could not create the Event! (Error %lu)\n", dwError);
342 return dwError;
343 }
344
345 hNoPendingInstalls = CreateEventW(NULL,
346 TRUE,
347 FALSE,
348 L"Global\\PnP_No_Pending_Install_Events");
349 if (hNoPendingInstalls == NULL)
350 {
351 dwError = GetLastError();
352 DPRINT1("Could not create the Event! (Error %lu)\n", dwError);
353 return dwError;
354 }
355
356 InitializeSListHead(&DeviceInstallListHead);
357
358 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
359 L"System\\CurrentControlSet\\Enum",
360 0,
361 KEY_ALL_ACCESS,
362 &hEnumKey);
363 if (dwError != ERROR_SUCCESS)
364 {
365 DPRINT1("Could not open the Enum Key! (Error %lu)\n", dwError);
366 return dwError;
367 }
368
369 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
370 L"System\\CurrentControlSet\\Control\\Class",
371 0,
372 KEY_ALL_ACCESS,
373 &hClassKey);
374 if (dwError != ERROR_SUCCESS)
375 {
376 DPRINT1("Could not open the Class Key! (Error %lu)\n", dwError);
377 return dwError;
378 }
379
380 DPRINT("UMPNPMGR: InitializePnPManager() done\n");
381
382 return 0;
383 }
384
385 BOOL WINAPI
386 DllMain(HINSTANCE hinstDLL,
387 DWORD fdwReason,
388 LPVOID lpvReserved)
389 {
390 switch (fdwReason)
391 {
392 case DLL_PROCESS_ATTACH:
393 DisableThreadLibraryCalls(hinstDLL);
394 InitializePnPManager();
395 break;
396
397 case DLL_PROCESS_DETACH:
398 break;
399 }
400
401 return TRUE;
402 }
403
404 /* EOF */