1 /*
2 * PROJECT: ReactOS Service Control Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/system/services/controlset.c
5 * PURPOSE: Control Set Management
6 * COPYRIGHT: Copyright 2012 Eric Kohl
7 *
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include "services.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 LSTATUS WINAPI RegCopyTreeW(_In_ HKEY, _In_opt_ LPCWSTR, _In_ HKEY);
18 LSTATUS WINAPI RegDeleteTreeW(_In_ HKEY, _In_opt_ LPCWSTR);
19
20 /* GLOBALS *******************************************************************/
21
22 static BOOL bBootAccepted = FALSE;
23
24
25 /* FUNCTIONS *****************************************************************/
26
27 static
28 DWORD
ScmGetControlSetValues(PDWORD pdwCurrentControlSet,PDWORD pdwDefaultControlSet,PDWORD pdwFailedControlSet,PDWORD pdwLastKnownGoodControlSet)29 ScmGetControlSetValues(
30 PDWORD pdwCurrentControlSet,
31 PDWORD pdwDefaultControlSet,
32 PDWORD pdwFailedControlSet,
33 PDWORD pdwLastKnownGoodControlSet)
34 {
35 HKEY hSelectKey;
36 DWORD dwType;
37 DWORD dwSize;
38 DWORD dwError;
39
40 DPRINT("ScmGetControlSetValues() called\n");
41
42 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
43 L"System\\Select",
44 0,
45 KEY_READ,
46 &hSelectKey);
47 if (dwError != ERROR_SUCCESS)
48 return dwError;
49
50 dwSize = sizeof(DWORD);
51 dwError = RegQueryValueExW(hSelectKey,
52 L"Current",
53 0,
54 &dwType,
55 (LPBYTE)pdwCurrentControlSet,
56 &dwSize);
57 if (dwError != ERROR_SUCCESS)
58 {
59 *pdwCurrentControlSet = 0;
60 }
61
62 dwSize = sizeof(DWORD);
63 dwError = RegQueryValueExW(hSelectKey,
64 L"Default",
65 0,
66 &dwType,
67 (LPBYTE)pdwDefaultControlSet,
68 &dwSize);
69 if (dwError != ERROR_SUCCESS)
70 {
71 *pdwDefaultControlSet = 0;
72 }
73
74 dwSize = sizeof(DWORD);
75 dwError = RegQueryValueExW(hSelectKey,
76 L"Failed",
77 0,
78 &dwType,
79 (LPBYTE)pdwFailedControlSet,
80 &dwSize);
81 if (dwError != ERROR_SUCCESS)
82 {
83 *pdwFailedControlSet = 0;
84 }
85
86 dwSize = sizeof(DWORD);
87 dwError = RegQueryValueExW(hSelectKey,
88 L"LastKnownGood",
89 0,
90 &dwType,
91 (LPBYTE)pdwLastKnownGoodControlSet,
92 &dwSize);
93 if (dwError != ERROR_SUCCESS)
94 {
95 *pdwLastKnownGoodControlSet = 0;
96 }
97
98 RegCloseKey(hSelectKey);
99
100 DPRINT("ControlSets:\n");
101 DPRINT("Current: %lu\n", *pdwCurrentControlSet);
102 DPRINT("Default: %lu\n", *pdwDefaultControlSet);
103 DPRINT("Failed: %lu\n", *pdwFailedControlSet);
104 DPRINT("LastKnownGood: %lu\n", *pdwLastKnownGoodControlSet);
105
106 return dwError;
107 }
108
109
110 static
111 DWORD
ScmSetLastKnownGoodControlSet(DWORD dwControlSet)112 ScmSetLastKnownGoodControlSet(
113 DWORD dwControlSet)
114 {
115 HKEY hSelectKey;
116 DWORD dwError;
117
118 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
119 L"System\\Select",
120 0,
121 KEY_WRITE,
122 &hSelectKey);
123 if (dwError != ERROR_SUCCESS)
124 return dwError;
125
126 dwError = RegSetValueExW(hSelectKey,
127 L"LastKnownGood",
128 0,
129 REG_DWORD,
130 (LPBYTE)&dwControlSet,
131 sizeof(dwControlSet));
132
133 RegFlushKey(hSelectKey);
134 RegCloseKey(hSelectKey);
135
136 return dwError;
137 }
138
139
140 static
141 DWORD
ScmGetSetupInProgress(VOID)142 ScmGetSetupInProgress(VOID)
143 {
144 DWORD dwError;
145 HKEY hKey;
146 DWORD dwType;
147 DWORD dwSize;
148 DWORD dwSetupInProgress = (DWORD)-1;
149
150 DPRINT("ScmGetSetupInProgress()\n");
151
152 /* Open key */
153 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
154 L"SYSTEM\\Setup",
155 0,
156 KEY_QUERY_VALUE,
157 &hKey);
158 if (dwError == ERROR_SUCCESS)
159 {
160 /* Read key */
161 dwSize = sizeof(DWORD);
162 RegQueryValueExW(hKey,
163 L"SystemSetupInProgress",
164 NULL,
165 &dwType,
166 (LPBYTE)&dwSetupInProgress,
167 &dwSize);
168 RegCloseKey(hKey);
169 }
170
171 DPRINT("SetupInProgress: %lu\n", dwSetupInProgress);
172 return dwSetupInProgress;
173 }
174
175
176 static
177 DWORD
ScmCopyControlSet(DWORD dwSourceControlSet,DWORD dwDestinationControlSet)178 ScmCopyControlSet(
179 DWORD dwSourceControlSet,
180 DWORD dwDestinationControlSet)
181 {
182 WCHAR szSourceControlSetName[32];
183 WCHAR szDestinationControlSetName[32];
184 HKEY hSourceControlSetKey = NULL;
185 HKEY hDestinationControlSetKey = NULL;
186 DWORD dwDisposition;
187 DWORD dwError;
188
189 /* Create the source control set name */
190 swprintf(szSourceControlSetName, L"SYSTEM\\ControlSet%03lu", dwSourceControlSet);
191 DPRINT("Source control set: %S\n", szSourceControlSetName);
192
193 /* Create the destination control set name */
194 swprintf(szDestinationControlSetName, L"SYSTEM\\ControlSet%03lu", dwDestinationControlSet);
195 DPRINT("Destination control set: %S\n", szDestinationControlSetName);
196
197 /* Open the source control set key */
198 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
199 szSourceControlSetName,
200 0,
201 KEY_READ,
202 &hSourceControlSetKey);
203 if (dwError != ERROR_SUCCESS)
204 goto done;
205
206 /* Create the destination control set key */
207 dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
208 szDestinationControlSetName,
209 0,
210 NULL,
211 REG_OPTION_NON_VOLATILE,
212 KEY_WRITE,
213 NULL,
214 &hDestinationControlSetKey,
215 &dwDisposition);
216 if (dwError != ERROR_SUCCESS)
217 goto done;
218
219 /* Copy the source control set to the destination control set */
220 dwError = RegCopyTreeW(hSourceControlSetKey,
221 NULL,
222 hDestinationControlSetKey);
223 if (dwError != ERROR_SUCCESS)
224 goto done;
225
226 RegFlushKey(hDestinationControlSetKey);
227
228 done:
229 if (hDestinationControlSetKey != NULL)
230 RegCloseKey(hDestinationControlSetKey);
231
232 if (hSourceControlSetKey != NULL)
233 RegCloseKey(hSourceControlSetKey);
234
235 return dwError;
236 }
237
238
239 static
240 DWORD
ScmDeleteControlSet(DWORD dwControlSet)241 ScmDeleteControlSet(
242 DWORD dwControlSet)
243 {
244 WCHAR szControlSetName[32];
245 HKEY hControlSetKey = NULL;
246 DWORD dwError;
247
248 DPRINT("ScmDeleteControSet(%lu)\n", dwControlSet);
249
250 /* Create the control set name */
251 swprintf(szControlSetName, L"SYSTEM\\ControlSet%03lu", dwControlSet);
252 DPRINT("Control set: %S\n", szControlSetName);
253
254 /* Open the system key */
255 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
256 szControlSetName,
257 0,
258 DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_SET_VALUE,
259 &hControlSetKey);
260 if (dwError != ERROR_SUCCESS)
261 return dwError;
262
263 /* Delete the control set */
264 dwError = RegDeleteTreeW(hControlSetKey,
265 NULL);
266
267 /* Open the system key */
268 RegCloseKey(hControlSetKey);
269
270 return dwError;
271 }
272
273
274 DWORD
ScmCreateLastKnownGoodControlSet(VOID)275 ScmCreateLastKnownGoodControlSet(VOID)
276 {
277 DWORD dwCurrentControlSet, dwDefaultControlSet;
278 DWORD dwFailedControlSet, dwLastKnownGoodControlSet;
279 DWORD dwNewControlSet;
280 DWORD dwError;
281
282 /* Get the control set values */
283 dwError = ScmGetControlSetValues(&dwCurrentControlSet,
284 &dwDefaultControlSet,
285 &dwFailedControlSet,
286 &dwLastKnownGoodControlSet);
287 if (dwError != ERROR_SUCCESS)
288 return dwError;
289
290 /* First boot after setup? */
291 if ((ScmGetSetupInProgress() == 0) &&
292 (dwCurrentControlSet == dwLastKnownGoodControlSet))
293 {
294 DPRINT("First boot after setup\n");
295
296 /* Search for a new control set number */
297 for (dwNewControlSet = 1; dwNewControlSet < 1000; dwNewControlSet++)
298 {
299 if ((dwNewControlSet != dwCurrentControlSet) &&
300 (dwNewControlSet != dwDefaultControlSet) &&
301 (dwNewControlSet != dwFailedControlSet) &&
302 (dwNewControlSet != dwLastKnownGoodControlSet))
303 break;
304 }
305
306 /* Fail if we did not find an unused control set!*/
307 if (dwNewControlSet >= 1000)
308 {
309 DPRINT1("Too many control sets\n");
310 return ERROR_NO_MORE_ITEMS;
311 }
312
313 /* Copy the current control set */
314 dwError = ScmCopyControlSet(dwCurrentControlSet,
315 dwNewControlSet);
316 if (dwError != ERROR_SUCCESS)
317 return dwError;
318
319 /* Set the new 'LastKnownGood' control set */
320 dwError = ScmSetLastKnownGoodControlSet(dwNewControlSet);
321 if (dwError == ERROR_SUCCESS)
322 {
323 /*
324 * Accept the boot here in order to prevent the creation of
325 * another control set when a user is going to get logged on
326 */
327 bBootAccepted = TRUE;
328 }
329 }
330
331 return dwError;
332 }
333
334
335 DWORD
ScmAcceptBoot(VOID)336 ScmAcceptBoot(VOID)
337 {
338 DWORD dwCurrentControlSet, dwDefaultControlSet;
339 DWORD dwFailedControlSet, dwLastKnownGoodControlSet;
340 DWORD dwNewControlSet;
341 DWORD dwError;
342
343 DPRINT("ScmAcceptBoot()\n");
344
345 if (bBootAccepted)
346 {
347 DPRINT1("Boot has already been accepted\n");
348 return ERROR_BOOT_ALREADY_ACCEPTED;
349 }
350
351 /* Get the control set values */
352 dwError = ScmGetControlSetValues(&dwCurrentControlSet,
353 &dwDefaultControlSet,
354 &dwFailedControlSet,
355 &dwLastKnownGoodControlSet);
356 if (dwError != ERROR_SUCCESS)
357 return dwError;
358
359 /* Search for a new control set number */
360 for (dwNewControlSet = 1; dwNewControlSet < 1000; dwNewControlSet++)
361 {
362 if ((dwNewControlSet != dwCurrentControlSet) &&
363 (dwNewControlSet != dwDefaultControlSet) &&
364 (dwNewControlSet != dwFailedControlSet) &&
365 (dwNewControlSet != dwLastKnownGoodControlSet))
366 break;
367 }
368
369 /* Fail if we did not find an unused control set!*/
370 if (dwNewControlSet >= 1000)
371 {
372 DPRINT1("Too many control sets\n");
373 return ERROR_NO_MORE_ITEMS;
374 }
375
376 /* Copy the current control set */
377 dwError = ScmCopyControlSet(dwCurrentControlSet,
378 dwNewControlSet);
379 if (dwError != ERROR_SUCCESS)
380 return dwError;
381
382 /* Delete the current last known good contol set, if it is not used anywhere else */
383 if ((dwLastKnownGoodControlSet != dwCurrentControlSet) &&
384 (dwLastKnownGoodControlSet != dwDefaultControlSet) &&
385 (dwLastKnownGoodControlSet != dwFailedControlSet))
386 {
387 ScmDeleteControlSet(dwLastKnownGoodControlSet);
388 }
389
390 /* Set the new 'LastKnownGood' control set */
391 dwError = ScmSetLastKnownGoodControlSet(dwNewControlSet);
392 if (dwError != ERROR_SUCCESS)
393 return dwError;
394
395 bBootAccepted = TRUE;
396
397 return ERROR_SUCCESS;
398 }
399
400
401 DWORD
ScmRunLastKnownGood(VOID)402 ScmRunLastKnownGood(VOID)
403 {
404 DPRINT("ScmRunLastKnownGood()\n");
405
406 if (bBootAccepted)
407 {
408 DPRINT1("Boot has already been accepted\n");
409 return ERROR_BOOT_ALREADY_ACCEPTED;
410 }
411
412 /* FIXME */
413
414 return ERROR_SUCCESS;
415 }
416
417 /* EOF */
418