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