xref: /reactos/base/system/services/controlset.c (revision 0db5d8a3)
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