[MSIEXEC] Sync with Wine Staging 2.16. CORE-13762
[reactos.git] / base / system / services / services.c
1 /*
2 * PROJECT: ReactOS Service Control Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/system/services/services.c
5 * PURPOSE: Main SCM controller
6 * COPYRIGHT: Copyright 2001-2005 Eric Kohl
7 * Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
8 *
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include "services.h"
14
15 #include <wincon.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20 int WINAPI RegisterServicesProcess(DWORD ServicesProcessId);
21
22 /* GLOBALS ******************************************************************/
23
24 /* Defined in include/reactos/services/services.h */
25 // #define SCM_START_EVENT L"SvcctrlStartEvent_A3752DX"
26 #define SCM_AUTOSTARTCOMPLETE_EVENT L"SC_AutoStartComplete"
27 #define LSA_RPC_SERVER_ACTIVE L"LSA_RPC_SERVER_ACTIVE"
28
29 BOOL ScmInitialize = FALSE;
30 BOOL ScmShutdown = FALSE;
31 static HANDLE hScmShutdownEvent = NULL;
32
33
34 /* FUNCTIONS *****************************************************************/
35
36 VOID
37 PrintString(LPCSTR fmt, ...)
38 {
39 #if DBG
40 CHAR buffer[512];
41 va_list ap;
42
43 va_start(ap, fmt);
44 vsprintf(buffer, fmt, ap);
45 va_end(ap);
46
47 OutputDebugStringA(buffer);
48 #endif
49 }
50
51
52 VOID
53 ScmLogEvent(DWORD dwEventId,
54 WORD wType,
55 WORD wStrings,
56 LPCWSTR *lpStrings)
57 {
58 HANDLE hLog;
59
60 hLog = RegisterEventSourceW(NULL,
61 L"Service Control Manager");
62 if (hLog == NULL)
63 {
64 DPRINT1("ScmLogEvent: RegisterEventSourceW failed %lu\n", GetLastError());
65 return;
66 }
67
68 if (!ReportEventW(hLog,
69 wType,
70 0,
71 dwEventId,
72 NULL,
73 wStrings,
74 0,
75 lpStrings,
76 NULL))
77 {
78 DPRINT1("ScmLogEvent: ReportEventW failed %lu\n", GetLastError());
79 }
80
81 DeregisterEventSource(hLog);
82 }
83
84
85 VOID
86 ScmWaitForLsa(VOID)
87 {
88 HANDLE hEvent = CreateEventW(NULL, TRUE, FALSE, LSA_RPC_SERVER_ACTIVE);
89 if (hEvent == NULL)
90 {
91 DPRINT1("Failed to create the notification event (Error %lu)\n", GetLastError());
92 }
93 else
94 {
95 DPRINT("Wait for the LSA server!\n");
96 WaitForSingleObject(hEvent, INFINITE);
97 DPRINT("LSA server running!\n");
98 CloseHandle(hEvent);
99 }
100
101 DPRINT("ScmWaitForLsa() done\n");
102 }
103
104
105 BOOL WINAPI
106 ShutdownHandlerRoutine(DWORD dwCtrlType)
107 {
108 DPRINT1("ShutdownHandlerRoutine() called\n");
109
110 if (dwCtrlType & (CTRL_SHUTDOWN_EVENT | CTRL_LOGOFF_EVENT))
111 {
112 DPRINT1("Shutdown event received!\n");
113 ScmShutdown = TRUE;
114
115 ScmAutoShutdownServices();
116 ScmShutdownServiceDatabase();
117
118 /* Set the shutdown event */
119 SetEvent(hScmShutdownEvent);
120 }
121
122 return TRUE;
123 }
124
125
126 /*** HACK CORE-12541: Special service accounts initialization HACK ************/
127
128 #include <ndk/setypes.h>
129 #include <sddl.h>
130 #include <userenv.h>
131 #include <strsafe.h>
132
133 /* Inspired from userenv.dll's CreateUserProfileExW and LoadUserProfileW APIs */
134 static
135 BOOL
136 ScmLogAccountHack(IN LPCWSTR pszAccountName,
137 IN LPCWSTR pszSid,
138 OUT PHKEY phProfile)
139 {
140 BOOL Success = FALSE;
141 LONG Error;
142 NTSTATUS Status;
143 BOOLEAN WasPriv1Set = FALSE, WasPriv2Set = FALSE;
144 PSID pSid;
145 DWORD dwLength;
146 WCHAR szUserHivePath[MAX_PATH];
147
148 DPRINT1("ScmLogAccountsHack(%S, %S)\n", pszAccountName, pszSid);
149 if (!pszAccountName || !pszSid || !phProfile)
150 return ERROR_INVALID_PARAMETER;
151
152 /* Convert the SID string into a SID. NOTE: No RTL equivalent. */
153 if (!ConvertStringSidToSidW(pszSid, &pSid))
154 {
155 DPRINT1("ConvertStringSidToSidW() failed (error %lu)\n", GetLastError());
156 return FALSE;
157 }
158
159 /* Determine a suitable profile path */
160 dwLength = ARRAYSIZE(szUserHivePath);
161 if (!GetProfilesDirectoryW(szUserHivePath, &dwLength))
162 {
163 DPRINT1("GetProfilesDirectoryW() failed (error %lu)\n", GetLastError());
164 goto Quit;
165 }
166
167 /* Create user hive name */
168 StringCbCatW(szUserHivePath, sizeof(szUserHivePath), L"\\");
169 StringCbCatW(szUserHivePath, sizeof(szUserHivePath), pszAccountName);
170 StringCbCatW(szUserHivePath, sizeof(szUserHivePath), L"\\ntuser.dat");
171 DPRINT("szUserHivePath: %S\n", szUserHivePath);
172
173 /* Magic #1: Create the special user profile if needed */
174 if (GetFileAttributesW(szUserHivePath) == INVALID_FILE_ATTRIBUTES)
175 {
176 if (!CreateUserProfileW(pSid, pszAccountName))
177 {
178 DPRINT1("CreateUserProfileW() failed (error %lu)\n", GetLastError());
179 goto Quit;
180 }
181 }
182
183 /*
184 * Now Da Magiks #2: Manually mount the user profile registry hive
185 * aka. manually do what LoadUserProfile does!! But we don't require
186 * a security token!
187 */
188
189 /* Acquire restore privilege */
190 Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &WasPriv1Set);
191 if (!NT_SUCCESS(Status))
192 {
193 DPRINT1("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Error 0x%08lx)\n", Status);
194 goto Quit;
195 }
196
197 /* Acquire backup privilege */
198 Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &WasPriv2Set);
199 if (!NT_SUCCESS(Status))
200 {
201 DPRINT1("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Error 0x%08lx)\n", Status);
202 RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, WasPriv1Set, FALSE, &WasPriv1Set);
203 goto Quit;
204 }
205
206 /* Load user registry hive */
207 Error = RegLoadKeyW(HKEY_USERS, pszSid, szUserHivePath);
208
209 /* Remove restore and backup privileges */
210 RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, WasPriv2Set, FALSE, &WasPriv2Set);
211 RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, WasPriv1Set, FALSE, &WasPriv1Set);
212
213 /* HACK: Do not fail if the profile has already been loaded! */
214 if (Error == ERROR_SHARING_VIOLATION)
215 Error = ERROR_SUCCESS;
216
217 if (Error != ERROR_SUCCESS)
218 {
219 DPRINT1("RegLoadKeyW() failed (Error %ld)\n", Error);
220 goto Quit;
221 }
222
223 /* Open future HKEY_CURRENT_USER */
224 Error = RegOpenKeyExW(HKEY_USERS,
225 pszSid,
226 0,
227 MAXIMUM_ALLOWED,
228 phProfile);
229 if (Error != ERROR_SUCCESS)
230 {
231 DPRINT1("RegOpenKeyExW() failed (Error %ld)\n", Error);
232 goto Quit;
233 }
234
235 Success = TRUE;
236
237 Quit:
238 LocalFree(pSid);
239
240 DPRINT1("ScmLogAccountsHack(%S) returned %s\n",
241 pszAccountName, Success ? "success" : "failure");
242
243 return Success;
244 }
245
246 static struct
247 {
248 LPCWSTR pszAccountName;
249 LPCWSTR pszSid;
250 HKEY hProfile;
251 } AccountHandles[] = {
252 // {L"LocalSystem" , L"S-1-5-18", NULL},
253 {L"LocalService" , L"S-1-5-19", NULL}, // L"NT AUTHORITY\\LocalService"
254 {L"NetworkService", L"S-1-5-20", NULL}, // L"NT AUTHORITY\\NetworkService"
255 };
256
257 static VOID
258 ScmCleanupServiceAccountsHack(VOID)
259 {
260 UINT i;
261
262 DPRINT1("ScmCleanupServiceAccountsHack()\n");
263
264 for (i = 0; i < ARRAYSIZE(AccountHandles); ++i)
265 {
266 if (AccountHandles[i].hProfile)
267 {
268 RegCloseKey(AccountHandles[i].hProfile);
269 AccountHandles[i].hProfile = NULL;
270 }
271 }
272 }
273
274 static BOOL
275 ScmApplyServiceAccountsHack(VOID)
276 {
277 UINT i;
278
279 DPRINT1("ScmApplyServiceAccountsHack()\n");
280
281 for (i = 0; i < ARRAYSIZE(AccountHandles); ++i)
282 {
283 if (!ScmLogAccountHack( AccountHandles[i].pszAccountName,
284 AccountHandles[i].pszSid,
285 &AccountHandles[i].hProfile))
286 {
287 ScmCleanupServiceAccountsHack();
288 return FALSE;
289 }
290 }
291
292 return TRUE;
293 }
294
295 /*************************** END OF HACK CORE-12541 ***************************/
296
297
298 int WINAPI
299 wWinMain(HINSTANCE hInstance,
300 HINSTANCE hPrevInstance,
301 LPWSTR lpCmdLine,
302 int nShowCmd)
303 {
304 HANDLE hScmStartEvent = NULL;
305 HANDLE hScmAutoStartCompleteEvent = NULL;
306 SC_RPC_LOCK Lock = NULL;
307 BOOL bCanDeleteNamedPipeCriticalSection = FALSE;
308 DWORD dwError;
309
310 DPRINT("SERVICES: Service Control Manager\n");
311
312 /* Make us critical */
313 RtlSetProcessIsCritical(TRUE, NULL, TRUE);
314
315 /* We are initializing ourselves */
316 ScmInitialize = TRUE;
317
318 /* Create the start event */
319 hScmStartEvent = CreateEventW(NULL, TRUE, FALSE, SCM_START_EVENT);
320 if (hScmStartEvent == NULL)
321 {
322 DPRINT1("SERVICES: Failed to create the start event\n");
323 goto done;
324 }
325 DPRINT("SERVICES: Created start event with handle %p.\n", hScmStartEvent);
326
327 /* Create the auto-start complete event */
328 hScmAutoStartCompleteEvent = CreateEventW(NULL, TRUE, FALSE, SCM_AUTOSTARTCOMPLETE_EVENT);
329 if (hScmAutoStartCompleteEvent == NULL)
330 {
331 DPRINT1("SERVICES: Failed to create the auto-start complete event\n");
332 goto done;
333 }
334 DPRINT("SERVICES: created auto-start complete event with handle %p.\n", hScmAutoStartCompleteEvent);
335
336 /* Create the shutdown event */
337 hScmShutdownEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
338 if (hScmShutdownEvent == NULL)
339 {
340 DPRINT1("SERVICES: Failed to create the shutdown event\n");
341 goto done;
342 }
343
344 /* Initialize our communication named pipe's critical section */
345 ScmInitNamedPipeCriticalSection();
346 bCanDeleteNamedPipeCriticalSection = TRUE;
347
348 // ScmInitThreadManager();
349
350 ScmInitializeSecurity();
351
352 /* FIXME: more initialization */
353
354 /* Read the control set values */
355 if (!ScmGetControlSetValues())
356 {
357 DPRINT1("SERVICES: Failed to read the control set values\n");
358 goto done;
359 }
360
361 /* Create the services database */
362 dwError = ScmCreateServiceDatabase();
363 if (dwError != ERROR_SUCCESS)
364 {
365 DPRINT1("SERVICES: Failed to create SCM database (Error %lu)\n", dwError);
366 goto done;
367 }
368
369 /* Wait for the LSA server */
370 ScmWaitForLsa();
371
372 /* Update the services database */
373 ScmGetBootAndSystemDriverState();
374
375 /* Register the Service Control Manager process with the ReactOS Subsystem */
376 if (!RegisterServicesProcess(GetCurrentProcessId()))
377 {
378 DPRINT1("SERVICES: Could not register SCM process\n");
379 goto done;
380 }
381
382 /*
383 * Acquire the user service start lock until
384 * auto-start services have been started.
385 */
386 dwError = ScmAcquireServiceStartLock(TRUE, &Lock);
387 if (dwError != ERROR_SUCCESS)
388 {
389 DPRINT1("SERVICES: Failed to acquire the service start lock (Error %lu)\n", dwError);
390 goto done;
391 }
392
393 /* Start the RPC server */
394 ScmStartRpcServer();
395
396 /* Signal start event */
397 SetEvent(hScmStartEvent);
398
399 DPRINT("SERVICES: Initialized.\n");
400
401 /* Register event handler (used for system shutdown) */
402 SetConsoleCtrlHandler(ShutdownHandlerRoutine, TRUE);
403
404 /*
405 * Set our shutdown parameters: we want to shutdown after the maintained
406 * services (that inherit the default shutdown level of 640).
407 */
408 SetProcessShutdownParameters(480, SHUTDOWN_NORETRY);
409
410 /*** HACK CORE-12541: Apply service accounts HACK ***/
411 ScmApplyServiceAccountsHack();
412
413 /* Start auto-start services */
414 ScmAutoStartServices();
415
416 /* Signal auto-start complete event */
417 SetEvent(hScmAutoStartCompleteEvent);
418
419 /* FIXME: more to do ? */
420
421 /* Release the service start lock */
422 ScmReleaseServiceStartLock(&Lock);
423
424 /* Initialization finished */
425 ScmInitialize = FALSE;
426
427 DPRINT("SERVICES: Running.\n");
428
429 /* Wait until the shutdown event gets signaled */
430 WaitForSingleObject(hScmShutdownEvent, INFINITE);
431
432 /*** HACK CORE-12541: Cleanup service accounts HACK ***/
433 ScmCleanupServiceAccountsHack();
434
435 done:
436 ScmShutdownSecurity();
437
438 /* Delete our communication named pipe's critical section */
439 if (bCanDeleteNamedPipeCriticalSection != FALSE)
440 ScmDeleteNamedPipeCriticalSection();
441
442 /* Close the shutdown event */
443 if (hScmShutdownEvent != NULL)
444 CloseHandle(hScmShutdownEvent);
445
446 /* Close the auto-start complete event */
447 if (hScmAutoStartCompleteEvent != NULL)
448 CloseHandle(hScmAutoStartCompleteEvent);
449
450 /* Close the start event */
451 if (hScmStartEvent != NULL)
452 CloseHandle(hScmStartEvent);
453
454 DPRINT("SERVICES: Finished.\n");
455
456 ExitThread(0);
457 return 0;
458 }
459
460 /* EOF */