1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Test for QueryServiceConfig2A/W
5  * PROGRAMMER:      Hermès BÉLUSCA - MAÏTO
6  */
7 
8 #include "precomp.h"
9 
10 #define TESTING_SERVICEW     L"Spooler"
11 #define TESTING_SERVICEA      "Spooler"
12 
13 /*
14  * Taken from base/system/services/config.c and adapted.
15  */
16 static DWORD
17 RegReadStringW(HKEY   hKey,
18                LPWSTR lpValueName,
19                LPWSTR *lpValue)
20 {
21     DWORD dwError;
22     DWORD dwSize;
23     DWORD dwType;
24 
25     *lpValue = NULL;
26 
27     dwSize  = 0;
28     dwError = RegQueryValueExW(hKey,
29                                lpValueName,
30                                0,
31                                &dwType,
32                                NULL,
33                                &dwSize);
34     if (dwError != ERROR_SUCCESS)
35         return dwError;
36 
37     *lpValue = HeapAlloc(GetProcessHeap(), 0, dwSize);
38     if (*lpValue == NULL)
39         return ERROR_NOT_ENOUGH_MEMORY;
40 
41     dwError = RegQueryValueExW(hKey,
42                                lpValueName,
43                                0,
44                                &dwType,
45                                (LPBYTE)*lpValue,
46                                &dwSize);
47     if (dwError != ERROR_SUCCESS)
48     {
49         HeapFree(GetProcessHeap(), 0, *lpValue);
50         *lpValue = NULL;
51     }
52 
53     return dwError;
54 }
55 
56 static DWORD
57 RegReadStringA(HKEY  hKey,
58                LPSTR lpValueName,
59                LPSTR *lpValue)
60 {
61     DWORD dwError;
62     DWORD dwSize;
63     DWORD dwType;
64 
65     *lpValue = NULL;
66 
67     dwSize  = 0;
68     dwError = RegQueryValueExA(hKey,
69                                lpValueName,
70                                0,
71                                &dwType,
72                                NULL,
73                                &dwSize);
74     if (dwError != ERROR_SUCCESS)
75         return dwError;
76 
77     *lpValue = HeapAlloc(GetProcessHeap(), 0, dwSize);
78     if (*lpValue == NULL)
79         return ERROR_NOT_ENOUGH_MEMORY;
80 
81     dwError = RegQueryValueExA(hKey,
82                                lpValueName,
83                                0,
84                                &dwType,
85                                (LPBYTE)*lpValue,
86                                &dwSize);
87     if (dwError != ERROR_SUCCESS)
88     {
89         HeapFree(GetProcessHeap(), 0, *lpValue);
90         *lpValue = NULL;
91     }
92 
93     return dwError;
94 }
95 
96 
97 static int QueryConfig2W(SC_HANDLE hService, LPCWSTR serviceName, DWORD dwInfoLevel)
98 {
99     int    iRet  = 0;
100     LONG   lRet  = 0;
101     DWORD  dwRet = 0;
102     BOOL   bError = FALSE;
103     DWORD  dwRequiredSize = 0;
104     LPBYTE lpBuffer = NULL;
105 
106     WCHAR keyName[256];
107     HKEY hKey = NULL;
108     DWORD dwType = 0;
109 
110     /* Get the needed size */
111     SetLastError(0xdeadbeef);
112     bError = QueryServiceConfig2W(hService,
113                                   dwInfoLevel,
114                                   NULL,
115                                   0,
116                                   &dwRequiredSize);
117     ok(bError == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INSUFFICIENT_BUFFER);
118     ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
119     if (dwRequiredSize == 0)
120     {
121         skip("Required size is null; cannot proceed with QueryConfig2W --> %lu test\n", dwInfoLevel);
122         return 1;
123     }
124 
125     /* Allocate memory */
126     lpBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
127     if (lpBuffer == NULL)
128     {
129         skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
130         return 2;
131     }
132 
133     /* Get the actual value */
134     SetLastError(0xdeadbeef);
135     bError = QueryServiceConfig2W(hService,
136                                   dwInfoLevel,
137                                   lpBuffer,
138                                   dwRequiredSize,
139                                   &dwRequiredSize);
140     ok(bError, "bError = %u, expected TRUE\n", bError);
141     if (bError == FALSE)
142     {
143         skip("QueryServiceConfig2W returned an error; cannot proceed with QueryConfig2W --> %lu test\n", dwInfoLevel);
144         HeapFree(GetProcessHeap(), 0, lpBuffer);
145         return 3;
146     }
147 
148     /* Now we can compare the retrieved value with what it's actually stored in the registry */
149     StringCbPrintfW(keyName, sizeof(keyName), L"System\\CurrentControlSet\\Services\\%s", serviceName);
150     SetLastError(0xdeadbeef);
151     lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, &hKey);
152     ok(lRet == ERROR_SUCCESS, "RegOpenKeyExW failed with 0x%08lx\n", lRet);
153     if (lRet != ERROR_SUCCESS)
154     {
155         skip("No regkey; cannot proceed with QueryConfig2W --> %lu test\n", dwInfoLevel);
156         HeapFree(GetProcessHeap(), 0, lpBuffer);
157         return 4;
158     }
159 
160     switch (dwInfoLevel)
161     {
162         case SERVICE_CONFIG_DESCRIPTION:
163         {
164             LPSERVICE_DESCRIPTIONW lpDescription = (LPSERVICE_DESCRIPTIONW)lpBuffer;
165             LPWSTR lpszDescription = NULL;
166 
167             /* Retrieve the description via the registry */
168             dwRet = RegReadStringW(hKey, L"Description", &lpszDescription);
169             ok(dwRet == ERROR_SUCCESS, "RegReadStringW returned 0x%08lx\n", dwRet);
170             ok(lpszDescription != NULL, "lpszDescription is null, expected non-null\n");
171 
172             /* Compare it with the description retrieved via QueryServiceConfig2 */
173             if (lpszDescription)
174                 iRet = wcscmp(lpDescription->lpDescription, lpszDescription);
175             else
176                 iRet = 0;
177 
178             ok(iRet == 0, "Retrieved descriptions are different !\n");
179 
180 
181             /* Memory cleanup */
182             HeapFree(GetProcessHeap(), 0, lpszDescription);
183 
184             break;
185         }
186 
187         case SERVICE_CONFIG_FAILURE_ACTIONS:
188         {
189             LPSERVICE_FAILURE_ACTIONSW lpFailureActions1 = (LPSERVICE_FAILURE_ACTIONSW)lpBuffer;
190             LPSERVICE_FAILURE_ACTIONSW lpFailureActions2 = NULL;
191             LPWSTR lpRebootMessage  = NULL;
192             LPWSTR lpFailureCommand = NULL;
193             DWORD  i = 0;
194 
195             /* Retrieve the failure actions via the registry */
196             lRet = RegQueryValueExW(hKey,
197                                     L"FailureActions",
198                                     NULL,
199                                     &dwType,
200                                     NULL,
201                                     &dwRequiredSize);
202             ok(lRet == ERROR_SUCCESS, "RegQueryValueExW returned 0x%08lx\n", lRet);
203             ok(dwType == REG_BINARY, "dwType = %lu, expected REG_BINARY\n", dwType);
204             ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
205 
206             lpFailureActions2 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
207             if (lpFailureActions2 == NULL)
208             {
209                 skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
210                 break;
211             }
212 
213             lRet = RegQueryValueExW(hKey,
214                                     L"FailureActions",
215                                     NULL,
216                                     NULL,
217                                     (LPBYTE)lpFailureActions2,
218                                     &dwRequiredSize);
219             ok(lRet == ERROR_SUCCESS, "RegQueryValueExW returned 0x%08lx\n", lRet);
220             ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
221 
222             /* Get the strings */
223             RegReadStringW(hKey, L"FailureCommand", &lpFailureCommand);
224             RegReadStringW(hKey, L"RebootMessage" , &lpRebootMessage );
225 
226             /* Check the values */
227             ok(lpFailureActions1->dwResetPeriod == lpFailureActions2->dwResetPeriod, "lpFailureActions1->dwResetPeriod != lpFailureActions2->dwResetPeriod\n");
228             ok(lpFailureActions1->cActions == lpFailureActions2->cActions, "lpFailureActions1->cActions != lpFailureActions2->cActions\n");
229 
230             /* Compare the actions */
231             if (lpFailureActions1->cActions == lpFailureActions2->cActions)
232             {
233                 lpFailureActions2->lpsaActions = (lpFailureActions2->cActions > 0 ? (LPSC_ACTION)(lpFailureActions2 + 1) : NULL);
234 
235                 if (lpFailureActions1->cActions > 0 &&
236                     lpFailureActions1->lpsaActions != NULL)
237                 {
238                     for (i = 0; i < lpFailureActions1->cActions; ++i)
239                     {
240                         ok(lpFailureActions1->lpsaActions[i].Type  == lpFailureActions2->lpsaActions[i].Type , "lpFailureActions1->lpsaActions[%lu].Type  != lpFailureActions2->lpsaActions[%lu].Type\n" , i, i);
241                         ok(lpFailureActions1->lpsaActions[i].Delay == lpFailureActions2->lpsaActions[i].Delay, "lpFailureActions1->lpsaActions[%lu].Delay != lpFailureActions2->lpsaActions[%lu].Delay\n", i, i);
242                     }
243                 }
244             }
245 
246             /* TODO: retrieve the strings if they are in MUI format */
247 
248             /* Compare RebootMsg */
249             if (lpFailureActions1->lpRebootMsg && lpRebootMessage)
250                 iRet = wcscmp(lpFailureActions1->lpRebootMsg, lpRebootMessage);
251             else
252                 iRet = 0;
253 
254             ok(iRet == 0, "Retrieved reboot messages are different !\n");
255 
256             /* Compare Command */
257             if (lpFailureActions1->lpCommand && lpFailureCommand)
258                 iRet = wcscmp(lpFailureActions1->lpCommand, lpFailureCommand);
259             else
260                 iRet = 0;
261 
262             ok(iRet == 0, "Retrieved commands are different !\n");
263 
264 
265             /* Memory cleanup */
266             if (lpRebootMessage)
267                 HeapFree(GetProcessHeap(), 0, lpRebootMessage);
268 
269             if (lpFailureCommand)
270                 HeapFree(GetProcessHeap(), 0, lpFailureCommand);
271 
272             HeapFree(GetProcessHeap(), 0, lpFailureActions2);
273 
274             break;
275         }
276 
277         default:
278             skip("Unknown dwInfoLevel %lu, cannot proceed with QueryConfig2W --> %lu test\n", dwInfoLevel, dwInfoLevel);
279             break;
280     }
281 
282     RegCloseKey(hKey);
283 
284     HeapFree(GetProcessHeap(), 0, lpBuffer);
285 
286     return 0;
287 }
288 
289 static int QueryConfig2A(SC_HANDLE hService, LPCSTR serviceName, DWORD dwInfoLevel)
290 {
291     int    iRet  = 0;
292     LONG   lRet  = 0;
293     DWORD  dwRet = 0;
294     BOOL   bError = FALSE;
295     DWORD  dwRequiredSize = 0;
296     LPBYTE lpBuffer = NULL;
297 
298     CHAR keyName[256];
299     HKEY hKey = NULL;
300     DWORD dwType = 0;
301 
302     /* Get the needed size */
303     SetLastError(0xdeadbeef);
304     bError = QueryServiceConfig2A(hService,
305                                   dwInfoLevel,
306                                   NULL,
307                                   0,
308                                   &dwRequiredSize);
309     ok(bError == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INSUFFICIENT_BUFFER);
310     ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
311     if (dwRequiredSize == 0)
312     {
313         skip("Required size is null; cannot proceed with QueryConfig2A --> %lu test\n", dwInfoLevel);
314         return 1;
315     }
316 
317     /* Allocate memory */
318     lpBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
319     if (lpBuffer == NULL)
320     {
321         skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
322         return 2;
323     }
324 
325     /* Get the actual value */
326     SetLastError(0xdeadbeef);
327     bError = QueryServiceConfig2A(hService,
328                                   dwInfoLevel,
329                                   lpBuffer,
330                                   dwRequiredSize,
331                                   &dwRequiredSize);
332     ok(bError, "bError = %u, expected TRUE\n", bError);
333     if (bError == FALSE)
334     {
335         skip("QueryServiceConfig2A returned an error; cannot proceed with QueryConfig2A --> %lu test\n", dwInfoLevel);
336         HeapFree(GetProcessHeap(), 0, lpBuffer);
337         return 3;
338     }
339 
340     /* Now we can compare the retrieved value with what it's actually stored in the registry */
341     StringCbPrintfA(keyName, sizeof(keyName), "System\\CurrentControlSet\\Services\\%s", serviceName);
342     SetLastError(0xdeadbeef);
343     lRet = RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, &hKey);
344     ok(lRet == ERROR_SUCCESS, "RegOpenKeyExA failed with 0x%08lx\n", lRet);
345     if (lRet != ERROR_SUCCESS)
346     {
347         skip("No regkey; cannot proceed with QueryConfig2A --> %lu test\n", dwInfoLevel);
348         HeapFree(GetProcessHeap(), 0, lpBuffer);
349         return 4;
350     }
351 
352     switch (dwInfoLevel)
353     {
354         case SERVICE_CONFIG_DESCRIPTION:
355         {
356             LPSERVICE_DESCRIPTIONA lpDescription = (LPSERVICE_DESCRIPTIONA)lpBuffer;
357             LPSTR lpszDescription = NULL;
358 
359             /* Retrieve the description via the registry */
360             dwRet = RegReadStringA(hKey, "Description", &lpszDescription);
361             ok(dwRet == ERROR_SUCCESS, "RegReadStringA returned 0x%08lx\n", dwRet);
362             ok(lpszDescription != NULL, "lpszDescription is null, expected non-null\n");
363 
364             /* Compare it with the description retrieved via QueryServiceConfig2 */
365             if (lpszDescription)
366                 iRet = strcmp(lpDescription->lpDescription, lpszDescription);
367             else
368                 iRet = 0;
369 
370             ok(iRet == 0, "Retrieved descriptions are different !\n");
371 
372 
373             /* Memory cleanup */
374             HeapFree(GetProcessHeap(), 0, lpszDescription);
375 
376             break;
377         }
378 
379         case SERVICE_CONFIG_FAILURE_ACTIONS:
380         {
381             LPSERVICE_FAILURE_ACTIONSA lpFailureActions1 = (LPSERVICE_FAILURE_ACTIONSA)lpBuffer;
382             LPSERVICE_FAILURE_ACTIONSA lpFailureActions2 = NULL;
383             LPSTR lpRebootMessage  = NULL;
384             LPSTR lpFailureCommand = NULL;
385             DWORD i = 0;
386 
387             /* Retrieve the failure actions via the registry */
388             lRet = RegQueryValueExA(hKey,
389                                     "FailureActions",
390                                     NULL,
391                                     &dwType,
392                                     NULL,
393                                     &dwRequiredSize);
394             ok(lRet == ERROR_SUCCESS, "RegQueryValueExA returned 0x%08lx\n", lRet);
395             ok(dwType == REG_BINARY, "dwType = %lu, expected REG_BINARY\n", dwType);
396             ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
397 
398             lpFailureActions2 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
399             if (lpFailureActions2 == NULL)
400             {
401                 skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
402                 break;
403             }
404 
405             lRet = RegQueryValueExA(hKey,
406                                     "FailureActions",
407                                     NULL,
408                                     NULL,
409                                     (LPBYTE)lpFailureActions2,
410                                     &dwRequiredSize);
411             ok(lRet == ERROR_SUCCESS, "RegQueryValueExA returned 0x%08lx\n", lRet);
412             ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
413 
414             /* Get the strings */
415             RegReadStringA(hKey, "FailureCommand", &lpFailureCommand);
416             RegReadStringA(hKey, "RebootMessage" , &lpRebootMessage );
417 
418             /* Check the values */
419             ok(lpFailureActions1->dwResetPeriod == lpFailureActions2->dwResetPeriod, "lpFailureActions1->dwResetPeriod != lpFailureActions2->dwResetPeriod\n");
420             ok(lpFailureActions1->cActions == lpFailureActions2->cActions, "lpFailureActions1->cActions != lpFailureActions2->cActions\n");
421 
422             /* Compare the actions */
423             if (lpFailureActions1->cActions == lpFailureActions2->cActions)
424             {
425                 lpFailureActions2->lpsaActions = (lpFailureActions2->cActions > 0 ? (LPSC_ACTION)(lpFailureActions2 + 1) : NULL);
426 
427                 if (lpFailureActions1->cActions > 0 &&
428                     lpFailureActions1->lpsaActions != NULL)
429                 {
430                     for (i = 0; i < lpFailureActions1->cActions; ++i)
431                     {
432                         ok(lpFailureActions1->lpsaActions[i].Type  == lpFailureActions2->lpsaActions[i].Type , "lpFailureActions1->lpsaActions[%lu].Type  != lpFailureActions2->lpsaActions[%lu].Type\n" , i, i);
433                         ok(lpFailureActions1->lpsaActions[i].Delay == lpFailureActions2->lpsaActions[i].Delay, "lpFailureActions1->lpsaActions[%lu].Delay != lpFailureActions2->lpsaActions[%lu].Delay\n", i, i);
434                     }
435                 }
436             }
437 
438             /* TODO: retrieve the strings if they are in MUI format */
439 
440             /* Compare RebootMsg */
441             if (lpFailureActions1->lpRebootMsg && lpRebootMessage)
442                 iRet = strcmp(lpFailureActions1->lpRebootMsg, lpRebootMessage);
443             else
444                 iRet = 0;
445 
446             ok(iRet == 0, "Retrieved reboot messages are different !\n");
447 
448             /* Compare Command */
449             if (lpFailureActions1->lpCommand && lpFailureCommand)
450                 iRet = strcmp(lpFailureActions1->lpCommand, lpFailureCommand);
451             else
452                 iRet = 0;
453 
454             ok(iRet == 0, "Retrieved commands are different !\n");
455 
456 
457             /* Memory cleanup */
458             if (lpRebootMessage)
459                 HeapFree(GetProcessHeap(), 0, lpRebootMessage);
460 
461             if (lpFailureCommand)
462                 HeapFree(GetProcessHeap(), 0, lpFailureCommand);
463 
464             HeapFree(GetProcessHeap(), 0, lpFailureActions2);
465 
466             break;
467         }
468 
469         default:
470             skip("Unknown dwInfoLevel %lu, cannot proceed with QueryConfig2A --> %lu test\n", dwInfoLevel, dwInfoLevel);
471             break;
472     }
473 
474     RegCloseKey(hKey);
475 
476     HeapFree(GetProcessHeap(), 0, lpBuffer);
477 
478     return 0;
479 }
480 
481 
482 static void Test_QueryServiceConfig2W(void)
483 {
484     SC_HANDLE hScm     = NULL;
485     SC_HANDLE hService = NULL;
486 
487     SetLastError(0xdeadbeef);
488     hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
489     ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError());
490     if (!hScm)
491     {
492         skip("No service control manager; cannot proceed with QueryServiceConfig2W test\n");
493         goto cleanup;
494     }
495 
496     ok_err(ERROR_SUCCESS);
497 
498     SetLastError(0xdeadbeef);
499     hService = OpenServiceW(hScm, TESTING_SERVICEW, SERVICE_QUERY_CONFIG);
500     ok(hService != NULL, "Failed to open service handle, error=0x%08lx\n", GetLastError());
501     if (!hService)
502     {
503         skip("Service not found; cannot proceed with QueryServiceConfig2W test\n");
504         goto cleanup;
505     }
506 
507     ok_err(ERROR_SUCCESS);
508 
509     if (QueryConfig2W(hService, TESTING_SERVICEW, SERVICE_CONFIG_DESCRIPTION) != 0)
510         goto cleanup;
511 
512     if (QueryConfig2W(hService, TESTING_SERVICEW, SERVICE_CONFIG_FAILURE_ACTIONS) != 0)
513         goto cleanup;
514 
515 cleanup:
516     if (hService)
517         CloseServiceHandle(hService);
518 
519     if (hScm)
520         CloseServiceHandle(hScm);
521 }
522 
523 static void Test_QueryServiceConfig2A(void)
524 {
525     SC_HANDLE hScm     = NULL;
526     SC_HANDLE hService = NULL;
527 
528     SetLastError(0xdeadbeef);
529     hScm = OpenSCManagerA(NULL, NULL, SC_MANAGER_CONNECT);
530     ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError());
531     if (!hScm)
532     {
533         skip("No service control manager; cannot proceed with QueryServiceConfig2A test\n");
534         goto cleanup;
535     }
536 
537     ok_err(ERROR_SUCCESS);
538 
539     SetLastError(0xdeadbeef);
540     hService = OpenServiceA(hScm, TESTING_SERVICEA, SERVICE_QUERY_CONFIG);
541     ok(hService != NULL, "Failed to open service handle, error=0x%08lx\n", GetLastError());
542     if (!hService)
543     {
544         skip("Service not found; cannot proceed with QueryServiceConfig2A test\n");
545         goto cleanup;
546     }
547 
548     ok_err(ERROR_SUCCESS);
549 
550     if (QueryConfig2A(hService, TESTING_SERVICEA, SERVICE_CONFIG_DESCRIPTION) != 0)
551         goto cleanup;
552 
553     if (QueryConfig2A(hService, TESTING_SERVICEA, SERVICE_CONFIG_FAILURE_ACTIONS) != 0)
554         goto cleanup;
555 
556 cleanup:
557     if (hService)
558         CloseServiceHandle(hService);
559 
560     if (hScm)
561         CloseServiceHandle(hScm);
562 }
563 
564 
565 START_TEST(QueryServiceConfig2)
566 {
567     Test_QueryServiceConfig2W();
568     Test_QueryServiceConfig2A();
569 }
570