xref: /reactos/base/system/services/controlset.c (revision 3f976713)
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 
18 /* GLOBALS *******************************************************************/
19 
20 static BOOL bBootAccepted = FALSE;
21 
22 
23 /* FUNCTIONS *****************************************************************/
24 
25 #if (_WIN32_WINNT < 0x0600)
26 static
27 DWORD
28 ScmCopyTree(
29     HKEY hSrcKey,
30     HKEY hDstKey)
31 {
32     DWORD dwSubKeys;
33     DWORD dwValues;
34     DWORD dwType;
35     DWORD dwMaxSubKeyNameLength;
36     DWORD dwSubKeyNameLength;
37     DWORD dwMaxValueNameLength;
38     DWORD dwValueNameLength;
39     DWORD dwMaxValueLength;
40     DWORD dwValueLength;
41     DWORD dwDisposition;
42     DWORD i;
43     LPWSTR lpNameBuffer;
44     LPBYTE lpDataBuffer;
45     HKEY hDstSubKey;
46     HKEY hSrcSubKey;
47     DWORD dwError;
48 
49     DPRINT("ScmCopyTree()\n");
50 
51     dwError = RegQueryInfoKey(hSrcKey,
52                               NULL,
53                               NULL,
54                               NULL,
55                               &dwSubKeys,
56                               &dwMaxSubKeyNameLength,
57                               NULL,
58                               &dwValues,
59                               &dwMaxValueNameLength,
60                               &dwMaxValueLength,
61                               NULL,
62                               NULL);
63     if (dwError != ERROR_SUCCESS)
64     {
65         DPRINT1("RegQueryInfoKey() failed (Error %lu)\n", dwError);
66         return dwError;
67     }
68 
69     dwMaxSubKeyNameLength++;
70     dwMaxValueNameLength++;
71 
72     DPRINT("dwSubKeys %lu\n", dwSubKeys);
73     DPRINT("dwMaxSubKeyNameLength %lu\n", dwMaxSubKeyNameLength);
74     DPRINT("dwValues %lu\n", dwValues);
75     DPRINT("dwMaxValueNameLength %lu\n", dwMaxValueNameLength);
76     DPRINT("dwMaxValueLength %lu\n", dwMaxValueLength);
77 
78     /* Copy subkeys */
79     if (dwSubKeys != 0)
80     {
81         lpNameBuffer = HeapAlloc(GetProcessHeap(),
82                                  0,
83                                  dwMaxSubKeyNameLength * sizeof(WCHAR));
84         if (lpNameBuffer == NULL)
85         {
86             DPRINT1("Buffer allocation failed\n");
87             return ERROR_NOT_ENOUGH_MEMORY;
88         }
89 
90         for (i = 0; i < dwSubKeys; i++)
91         {
92             dwSubKeyNameLength = dwMaxSubKeyNameLength;
93             dwError = RegEnumKeyExW(hSrcKey,
94                                     i,
95                                     lpNameBuffer,
96                                     &dwSubKeyNameLength,
97                                     NULL,
98                                     NULL,
99                                     NULL,
100                                     NULL);
101             if (dwError != ERROR_SUCCESS)
102             {
103                 DPRINT1("Subkey enumeration failed (Error %lu)\n", dwError);
104                 HeapFree(GetProcessHeap(),
105                          0,
106                          lpNameBuffer);
107                 return dwError;
108             }
109 
110             dwError = RegCreateKeyExW(hDstKey,
111                                       lpNameBuffer,
112                                       0,
113                                       NULL,
114                                       REG_OPTION_NON_VOLATILE,
115                                       KEY_WRITE,
116                                       NULL,
117                                       &hDstSubKey,
118                                       &dwDisposition);
119             if (dwError != ERROR_SUCCESS)
120             {
121                 DPRINT1("Subkey creation failed (Error %lu)\n", dwError);
122                 HeapFree(GetProcessHeap(),
123                          0,
124                          lpNameBuffer);
125                 return dwError;
126             }
127 
128             dwError = RegOpenKeyExW(hSrcKey,
129                                     lpNameBuffer,
130                                     0,
131                                     KEY_READ,
132                                     &hSrcSubKey);
133             if (dwError != ERROR_SUCCESS)
134             {
135                 DPRINT1("Error: %lu\n", dwError);
136                 RegCloseKey(hDstSubKey);
137                 HeapFree(GetProcessHeap(),
138                          0,
139                          lpNameBuffer);
140                 return dwError;
141             }
142 
143             dwError = ScmCopyTree(hSrcSubKey,
144                                   hDstSubKey);
145             if (dwError != ERROR_SUCCESS)
146             {
147                 DPRINT1("Error: %lu\n", dwError);
148                 RegCloseKey (hSrcSubKey);
149                 RegCloseKey (hDstSubKey);
150                 HeapFree(GetProcessHeap(),
151                          0,
152                          lpNameBuffer);
153                 return dwError;
154             }
155 
156             RegCloseKey(hSrcSubKey);
157             RegCloseKey(hDstSubKey);
158         }
159 
160         HeapFree(GetProcessHeap(),
161                  0,
162                  lpNameBuffer);
163     }
164 
165     /* Copy values */
166     if (dwValues != 0)
167     {
168         lpNameBuffer = HeapAlloc(GetProcessHeap(),
169                                  0,
170                                  dwMaxValueNameLength * sizeof(WCHAR));
171         if (lpNameBuffer == NULL)
172         {
173             DPRINT1("Buffer allocation failed\n");
174             return ERROR_NOT_ENOUGH_MEMORY;
175         }
176 
177         /* RegSetValueExW tries to read behind the maximum length, so give it space for that */
178         lpDataBuffer = HeapAlloc(GetProcessHeap(),
179                                  HEAP_ZERO_MEMORY,
180                                  dwMaxValueLength + sizeof(WCHAR));
181         if (lpDataBuffer == NULL)
182         {
183             DPRINT1("Buffer allocation failed\n");
184             HeapFree(GetProcessHeap(),
185                      0,
186                      lpNameBuffer);
187             return ERROR_NOT_ENOUGH_MEMORY;
188         }
189 
190         for (i = 0; i < dwValues; i++)
191         {
192             dwValueNameLength = dwMaxValueNameLength;
193             dwValueLength = dwMaxValueLength;
194             dwError = RegEnumValueW(hSrcKey,
195                                     i,
196                                     lpNameBuffer,
197                                     &dwValueNameLength,
198                                     NULL,
199                                     &dwType,
200                                     lpDataBuffer,
201                                     &dwValueLength);
202             if (dwError != ERROR_SUCCESS)
203             {
204                 DPRINT1("Error: %lu\n", dwError);
205                 HeapFree(GetProcessHeap(),
206                          0,
207                          lpDataBuffer);
208                 HeapFree(GetProcessHeap(),
209                          0,
210                          lpNameBuffer);
211                 return dwError;
212             }
213 
214             dwError = RegSetValueExW(hDstKey,
215                                      lpNameBuffer,
216                                      0,
217                                      dwType,
218                                      lpDataBuffer,
219                                      dwValueLength);
220             if (dwError != ERROR_SUCCESS)
221             {
222                 DPRINT1("Error: %lu\n", dwError);
223                 HeapFree(GetProcessHeap(),
224                          0,
225                          lpDataBuffer);
226                 HeapFree(GetProcessHeap(),
227                          0,
228                          lpNameBuffer);
229                 return dwError;
230             }
231         }
232 
233         HeapFree(GetProcessHeap(),
234                  0,
235                  lpDataBuffer);
236 
237         HeapFree(GetProcessHeap(),
238                  0,
239                  lpNameBuffer);
240     }
241 
242     DPRINT("ScmCopyTree() done\n");
243 
244     return ERROR_SUCCESS;
245 }
246 
247 
248 DWORD
249 ScmDeleteTree(
250     HKEY hKey,
251     PCWSTR pszSubKey)
252 {
253     DWORD dwMaxSubkeyLength, dwMaxValueLength;
254     DWORD dwMaxLength, dwSize;
255     PWSTR pszName = NULL;
256     HKEY hSubKey = NULL;
257     DWORD dwError;
258 
259     if (pszSubKey != NULL)
260     {
261         dwError = RegOpenKeyExW(hKey, pszSubKey, 0, KEY_READ, &hSubKey);
262         if (dwError != ERROR_SUCCESS)
263             return dwError;
264     }
265     else
266     {
267          hSubKey = hKey;
268     }
269 
270     /* Get highest length for keys, values */
271     dwError = RegQueryInfoKeyW(hSubKey,
272                                NULL,
273                                NULL,
274                                NULL,
275                                NULL,
276                                &dwMaxSubkeyLength,
277                                NULL,
278                                NULL,
279                                &dwMaxValueLength,
280                                NULL,
281                                NULL,
282                                NULL);
283     if (dwError != ERROR_SUCCESS)
284         goto done;
285 
286     dwMaxSubkeyLength++;
287     dwMaxValueLength++;
288     dwMaxLength = max(dwMaxSubkeyLength, dwMaxValueLength);
289 
290     /* Allocate a buffer for key and value names */
291     pszName = HeapAlloc(GetProcessHeap(),
292                          0,
293                          dwMaxLength * sizeof(WCHAR));
294     if (pszName == NULL)
295     {
296         dwError = ERROR_NOT_ENOUGH_MEMORY;
297         goto done;
298     }
299 
300     /* Recursively delete all the subkeys */
301     while (TRUE)
302     {
303         dwSize = dwMaxLength;
304         if (RegEnumKeyExW(hSubKey,
305                           0,
306                           pszName,
307                           &dwSize,
308                           NULL,
309                           NULL,
310                           NULL,
311                           NULL))
312             break;
313 
314         dwError = ScmDeleteTree(hSubKey, pszName);
315         if (dwError != ERROR_SUCCESS)
316             goto done;
317     }
318 
319     if (pszSubKey != NULL)
320     {
321         dwError = RegDeleteKeyW(hKey, pszSubKey);
322     }
323     else
324     {
325         while (TRUE)
326         {
327             dwSize = dwMaxLength;
328             if (RegEnumValueW(hKey,
329                               0,
330                               pszName,
331                               &dwSize,
332                               NULL,
333                               NULL,
334                               NULL,
335                               NULL))
336                 break;
337 
338             dwError = RegDeleteValueW(hKey, pszName);
339             if (dwError != ERROR_SUCCESS)
340                 goto done;
341         }
342     }
343 
344 done:
345     if (pszName != NULL)
346         HeapFree(GetProcessHeap(), 0, pszName);
347 
348     if (pszSubKey != NULL)
349         RegCloseKey(hSubKey);
350 
351     return dwError;
352 }
353 #endif
354 
355 
356 static
357 DWORD
358 ScmGetControlSetValues(
359     PDWORD pdwCurrentControlSet,
360     PDWORD pdwDefaultControlSet,
361     PDWORD pdwFailedControlSet,
362     PDWORD pdwLastKnownGoodControlSet)
363 {
364     HKEY hSelectKey;
365     DWORD dwType;
366     DWORD dwSize;
367     DWORD dwError;
368 
369     DPRINT("ScmGetControlSetValues() called\n");
370 
371     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
372                             L"System\\Select",
373                             0,
374                             KEY_READ,
375                             &hSelectKey);
376     if (dwError != ERROR_SUCCESS)
377         return dwError;
378 
379     dwSize = sizeof(DWORD);
380     dwError = RegQueryValueExW(hSelectKey,
381                                L"Current",
382                                0,
383                                &dwType,
384                                (LPBYTE)pdwCurrentControlSet,
385                                &dwSize);
386     if (dwError != ERROR_SUCCESS)
387     {
388         *pdwCurrentControlSet = 0;
389     }
390 
391     dwSize = sizeof(DWORD);
392     dwError = RegQueryValueExW(hSelectKey,
393                                L"Default",
394                                0,
395                                &dwType,
396                                (LPBYTE)pdwDefaultControlSet,
397                                &dwSize);
398     if (dwError != ERROR_SUCCESS)
399     {
400         *pdwDefaultControlSet = 0;
401     }
402 
403     dwSize = sizeof(DWORD);
404     dwError = RegQueryValueExW(hSelectKey,
405                                L"Failed",
406                                0,
407                                &dwType,
408                                (LPBYTE)pdwFailedControlSet,
409                                &dwSize);
410     if (dwError != ERROR_SUCCESS)
411     {
412         *pdwFailedControlSet = 0;
413     }
414 
415     dwSize = sizeof(DWORD);
416     dwError = RegQueryValueExW(hSelectKey,
417                                L"LastKnownGood",
418                                0,
419                                &dwType,
420                                (LPBYTE)pdwLastKnownGoodControlSet,
421                                &dwSize);
422     if (dwError != ERROR_SUCCESS)
423     {
424         *pdwLastKnownGoodControlSet = 0;
425     }
426 
427     RegCloseKey(hSelectKey);
428 
429     DPRINT("ControlSets:\n");
430     DPRINT("Current: %lu\n", *pdwCurrentControlSet);
431     DPRINT("Default: %lu\n", *pdwDefaultControlSet);
432     DPRINT("Failed: %lu\n", *pdwFailedControlSet);
433     DPRINT("LastKnownGood: %lu\n", *pdwLastKnownGoodControlSet);
434 
435     return dwError;
436 }
437 
438 
439 static
440 DWORD
441 ScmSetLastKnownGoodControlSet(
442     DWORD dwControlSet)
443 {
444     HKEY hSelectKey;
445     DWORD dwError;
446 
447     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
448                             L"System\\Select",
449                             0,
450                             KEY_WRITE,
451                             &hSelectKey);
452     if (dwError != ERROR_SUCCESS)
453         return dwError;
454 
455     dwError = RegSetValueExW(hSelectKey,
456                              L"LastKnownGood",
457                              0,
458                              REG_DWORD,
459                              (LPBYTE)&dwControlSet,
460                              sizeof(dwControlSet));
461 
462     RegFlushKey(hSelectKey);
463     RegCloseKey(hSelectKey);
464 
465     return dwError;
466 }
467 
468 
469 static
470 DWORD
471 ScmGetSetupInProgress(VOID)
472 {
473     DWORD dwError;
474     HKEY hKey;
475     DWORD dwType;
476     DWORD dwSize;
477     DWORD dwSetupInProgress = (DWORD)-1;
478 
479     DPRINT("ScmGetSetupInProgress()\n");
480 
481     /* Open key */
482     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
483                             L"SYSTEM\\Setup",
484                             0,
485                             KEY_QUERY_VALUE,
486                             &hKey);
487     if (dwError == ERROR_SUCCESS)
488     {
489         /* Read key */
490         dwSize = sizeof(DWORD);
491         RegQueryValueExW(hKey,
492                          L"SystemSetupInProgress",
493                          NULL,
494                          &dwType,
495                          (LPBYTE)&dwSetupInProgress,
496                          &dwSize);
497         RegCloseKey(hKey);
498     }
499 
500     DPRINT("SetupInProgress: %lu\n", dwSetupInProgress);
501     return dwSetupInProgress;
502 }
503 
504 
505 static
506 DWORD
507 ScmCopyControlSet(
508     DWORD dwSourceControlSet,
509     DWORD dwDestinationControlSet)
510 {
511     WCHAR szSourceControlSetName[32];
512     WCHAR szDestinationControlSetName[32];
513     HKEY hSourceControlSetKey = NULL;
514     HKEY hDestinationControlSetKey = NULL;
515     DWORD dwDisposition;
516     DWORD dwError;
517 
518     /* Create the source control set name */
519     swprintf(szSourceControlSetName, L"SYSTEM\\ControlSet%03lu", dwSourceControlSet);
520     DPRINT("Source control set: %S\n", szSourceControlSetName);
521 
522     /* Create the destination control set name */
523     swprintf(szDestinationControlSetName, L"SYSTEM\\ControlSet%03lu", dwDestinationControlSet);
524     DPRINT("Destination control set: %S\n", szDestinationControlSetName);
525 
526     /* Open the source control set key */
527     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
528                             szSourceControlSetName,
529                             0,
530                             KEY_READ,
531                             &hSourceControlSetKey);
532     if (dwError != ERROR_SUCCESS)
533         goto done;
534 
535     /* Create the destination control set key */
536     dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
537                               szDestinationControlSetName,
538                               0,
539                               NULL,
540                               REG_OPTION_NON_VOLATILE,
541                               KEY_WRITE,
542                               NULL,
543                               &hDestinationControlSetKey,
544                               &dwDisposition);
545     if (dwError != ERROR_SUCCESS)
546         goto done;
547 
548     /* Copy the source control set to the destination control set */
549 #if (_WIN32_WINNT >= 0x0600)
550     dwError = RegCopyTreeW(hSourceControlSetKey,
551                            NULL,
552                            hDestinationControlSetKey);
553 #else
554     dwError = ScmCopyTree(hSourceControlSetKey,
555                           hDestinationControlSetKey);
556 #endif
557     if (dwError != ERROR_SUCCESS)
558         goto done;
559 
560     RegFlushKey(hDestinationControlSetKey);
561 
562 done:
563     if (hDestinationControlSetKey != NULL)
564         RegCloseKey(hDestinationControlSetKey);
565 
566     if (hSourceControlSetKey != NULL)
567         RegCloseKey(hSourceControlSetKey);
568 
569     return dwError;
570 }
571 
572 
573 static
574 DWORD
575 ScmDeleteControlSet(
576     DWORD dwControlSet)
577 {
578     WCHAR szControlSetName[32];
579     HKEY hControlSetKey = NULL;
580     DWORD dwError;
581 
582     DPRINT("ScmDeleteControSet(%lu)\n", dwControlSet);
583 
584     /* Create the control set name */
585     swprintf(szControlSetName, L"SYSTEM\\ControlSet%03lu", dwControlSet);
586     DPRINT("Control set: %S\n", szControlSetName);
587 
588     /* Open the system key */
589     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
590                             szControlSetName,
591                             0,
592                             DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_SET_VALUE,
593                             &hControlSetKey);
594     if (dwError != ERROR_SUCCESS)
595         return dwError;
596 
597     /* Delete the control set */
598 #if (_WIN32_WINNT >= 0x0600)
599     dwError = RegDeleteTreeW(hControlSetKey,
600                              NULL);
601 #else
602     dwError = ScmDeleteTree(hControlSetKey,
603                             NULL);
604 #endif
605 
606     /* Open the system key */
607     RegCloseKey(hControlSetKey);
608 
609     return dwError;
610 }
611 
612 
613 DWORD
614 ScmCreateLastKnownGoodControlSet(VOID)
615 {
616     DWORD dwCurrentControlSet, dwDefaultControlSet;
617     DWORD dwFailedControlSet, dwLastKnownGoodControlSet;
618     DWORD dwNewControlSet;
619     DWORD dwError;
620 
621     /* Get the control set values */
622     dwError = ScmGetControlSetValues(&dwCurrentControlSet,
623                                      &dwDefaultControlSet,
624                                      &dwFailedControlSet,
625                                      &dwLastKnownGoodControlSet);
626     if (dwError != ERROR_SUCCESS)
627         return dwError;
628 
629     /* First boot after setup? */
630     if ((ScmGetSetupInProgress() == 0) &&
631         (dwCurrentControlSet == dwLastKnownGoodControlSet))
632     {
633         DPRINT("First boot after setup!\n");
634 
635         /* Search for a new control set number */
636         for (dwNewControlSet = 1; dwNewControlSet < 1000; dwNewControlSet++)
637         {
638             if ((dwNewControlSet != dwCurrentControlSet) &&
639                 (dwNewControlSet != dwDefaultControlSet) &&
640                 (dwNewControlSet != dwFailedControlSet) &&
641                 (dwNewControlSet != dwLastKnownGoodControlSet))
642                 break;
643         }
644 
645         /* Fail if we did not find an unused control set!*/
646         if (dwNewControlSet >= 1000)
647         {
648             DPRINT1("Too many control sets!\n");
649             return ERROR_NO_MORE_ITEMS;
650         }
651 
652         /* Copy the current control set */
653         dwError = ScmCopyControlSet(dwCurrentControlSet,
654                                     dwNewControlSet);
655         if (dwError != ERROR_SUCCESS)
656             return dwError;
657 
658         /* Set the new 'LastKnownGood' control set */
659         dwError = ScmSetLastKnownGoodControlSet(dwNewControlSet);
660         if (dwError == ERROR_SUCCESS)
661         {
662             /*
663              * Accept the boot here in order to prevent the creation of
664              * another control set when a user is going to get logged on
665              */
666             bBootAccepted = TRUE;
667         }
668     }
669 
670     return dwError;
671 }
672 
673 
674 DWORD
675 ScmAcceptBoot(VOID)
676 {
677     DWORD dwCurrentControlSet, dwDefaultControlSet;
678     DWORD dwFailedControlSet, dwLastKnownGoodControlSet;
679     DWORD dwNewControlSet;
680     DWORD dwError;
681 
682     DPRINT("ScmAcceptBoot()\n");
683 
684     if (bBootAccepted)
685     {
686         DPRINT1("Boot has alread been accepted!\n");
687         return ERROR_BOOT_ALREADY_ACCEPTED;
688     }
689 
690     /* Get the control set values */
691     dwError = ScmGetControlSetValues(&dwCurrentControlSet,
692                                      &dwDefaultControlSet,
693                                      &dwFailedControlSet,
694                                      &dwLastKnownGoodControlSet);
695     if (dwError != ERROR_SUCCESS)
696         return dwError;
697 
698     /* Search for a new control set number */
699     for (dwNewControlSet = 1; dwNewControlSet < 1000; dwNewControlSet++)
700     {
701         if ((dwNewControlSet != dwCurrentControlSet) &&
702             (dwNewControlSet != dwDefaultControlSet) &&
703             (dwNewControlSet != dwFailedControlSet) &&
704             (dwNewControlSet != dwLastKnownGoodControlSet))
705             break;
706     }
707 
708     /* Fail if we did not find an unused control set!*/
709     if (dwNewControlSet >= 1000)
710     {
711         DPRINT1("Too many control sets!\n");
712         return ERROR_NO_MORE_ITEMS;
713     }
714 
715     /* Copy the current control set */
716     dwError = ScmCopyControlSet(dwCurrentControlSet,
717                                 dwNewControlSet);
718     if (dwError != ERROR_SUCCESS)
719         return dwError;
720 
721     /* Delete the current last known good contol set, if it is not used anywhere else */
722     if ((dwLastKnownGoodControlSet != dwCurrentControlSet) &&
723         (dwLastKnownGoodControlSet != dwDefaultControlSet) &&
724         (dwLastKnownGoodControlSet != dwFailedControlSet))
725     {
726         ScmDeleteControlSet(dwLastKnownGoodControlSet);
727     }
728 
729     /* Set the new 'LastKnownGood' control set */
730     dwError = ScmSetLastKnownGoodControlSet(dwNewControlSet);
731     if (dwError != ERROR_SUCCESS)
732         return dwError;
733 
734     bBootAccepted = TRUE;
735 
736     return ERROR_SUCCESS;
737 }
738 
739 
740 DWORD
741 ScmRunLastKnownGood(VOID)
742 {
743     DPRINT("ScmRunLastKnownGood()\n");
744 
745     if (bBootAccepted)
746     {
747         DPRINT1("Boot has alread been accepted!\n");
748         return ERROR_BOOT_ALREADY_ACCEPTED;
749     }
750 
751     /* FIXME */
752 
753     return ERROR_SUCCESS;
754 }
755 
756 /* EOF */
757