1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Test for service process environment block
5  * PROGRAMMER:      Hermes Belusca-Maito
6  */
7 
8 #include "precomp.h"
9 
10 #include "svchlp.h"
11 
12 
13 /*** Service part of the test ***/
14 
15 static SERVICE_STATUS_HANDLE status_handle;
16 
17 static void
18 report_service_status(DWORD dwCurrentState,
19                       DWORD dwWin32ExitCode,
20                       DWORD dwWaitHint)
21 {
22     BOOL res;
23     SERVICE_STATUS status;
24 
25     status.dwServiceType   = SERVICE_WIN32_OWN_PROCESS;
26     status.dwCurrentState  = dwCurrentState;
27     status.dwWin32ExitCode = dwWin32ExitCode;
28     status.dwWaitHint      = dwWaitHint;
29 
30     status.dwServiceSpecificExitCode = 0;
31     status.dwCheckPoint              = 0;
32 
33     if ( (dwCurrentState == SERVICE_START_PENDING) ||
34          (dwCurrentState == SERVICE_STOP_PENDING)  ||
35          (dwCurrentState == SERVICE_STOPPED) )
36     {
37         status.dwControlsAccepted = 0;
38     }
39     else
40     {
41         status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
42     }
43 
44 #if 0
45     if ( (dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED) )
46         status.dwCheckPoint = 0;
47     else
48         status.dwCheckPoint = dwCheckPoint++;
49 #endif
50 
51     res = SetServiceStatus(status_handle, &status);
52     service_ok(res, "SetServiceStatus(%d) failed: %lu\n", dwCurrentState, GetLastError());
53 }
54 
55 static VOID WINAPI service_handler(DWORD ctrl)
56 {
57     switch(ctrl)
58     {
59     case SERVICE_CONTROL_STOP:
60     case SERVICE_CONTROL_SHUTDOWN:
61         report_service_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
62     default:
63         report_service_status(SERVICE_RUNNING, NO_ERROR, 0);
64     }
65 }
66 
67 static void WINAPI
68 service_main(DWORD dwArgc, LPWSTR* lpszArgv)
69 {
70     // SERVICE_STATUS_HANDLE status_handle;
71     LPWSTR lpEnvironment, lpEnvStr;
72     DWORD dwSize;
73     PTEB Teb;
74 
75     UNREFERENCED_PARAMETER(dwArgc);
76     UNREFERENCED_PARAMETER(lpszArgv);
77 
78     /* Register our service for control (lpszArgv[0] holds the service name) */
79     status_handle = RegisterServiceCtrlHandlerW(lpszArgv[0], service_handler);
80     service_ok(status_handle != NULL, "RegisterServiceCtrlHandler failed: %lu\n", GetLastError());
81     if (!status_handle)
82         return;
83 
84     /* Report SERVICE_RUNNING status */
85     report_service_status(SERVICE_RUNNING, NO_ERROR, 4000);
86 
87     /* Display our current environment for informative purposes */
88     lpEnvironment = GetEnvironmentStringsW();
89     lpEnvStr = lpEnvironment;
90     while (*lpEnvStr)
91     {
92         service_trace("%S\n", lpEnvStr);
93         lpEnvStr += wcslen(lpEnvStr) + 1;
94     }
95     FreeEnvironmentStringsW(lpEnvironment);
96 
97     /* Check the presence of the user-related environment variables */
98     dwSize = GetEnvironmentVariableW(L"ALLUSERSPROFILE", NULL, 0);
99     service_ok(dwSize != 0, "ALLUSERSPROFILE envvar not found, or GetEnvironmentVariableW failed: %lu\n", GetLastError());
100     dwSize = GetEnvironmentVariableW(L"USERPROFILE", NULL, 0);
101     service_ok(dwSize != 0, "USERPROFILE envvar not found, or GetEnvironmentVariableW failed: %lu\n", GetLastError());
102 #if 0 // May not always exist
103     dwSize = GetEnvironmentVariableW(L"USERNAME", NULL, 0);
104     service_ok(dwSize != 0, "USERNAME envvar not found, or GetEnvironmentVariableW failed: %lu\n", GetLastError());
105 #endif
106 
107     Teb = NtCurrentTeb();
108     service_ok(Teb->SubProcessTag != 0, "SubProcessTag is not defined!\n");
109     if (Teb->SubProcessTag != 0)
110     {
111         ULONG (NTAPI *_I_QueryTagInformation)(PVOID, DWORD, PVOID) = (PVOID)GetProcAddress(GetModuleHandle("advapi32.dll"), "I_QueryTagInformation");
112         if (_I_QueryTagInformation != NULL)
113         {
114             /* IN/OUT parameter structure for I_QueryTagInformation() function
115              * See: https://wj32.org/wp/2010/03/30/howto-use-i_querytaginformation/
116              * See: https://github.com/processhacker/processhacker/blob/master/phnt/include/subprocesstag.h
117              */
118             struct
119             {
120                 ULONG ProcessId;
121                 PVOID ServiceTag;
122                 ULONG TagType;
123                 PWSTR Buffer;
124             } ServiceQuery;
125 
126             /* Set our input parameters */
127             ServiceQuery.ProcessId = GetCurrentProcessId();
128             ServiceQuery.ServiceTag = Teb->SubProcessTag;
129             ServiceQuery.TagType = 0;
130             ServiceQuery.Buffer = NULL;
131             /* Call ADVAPI32 to query the correctness of our tag */
132             _I_QueryTagInformation(NULL, 1, &ServiceQuery);
133 
134             /* If buffer is not NULL, call succeed */
135             if (ServiceQuery.Buffer != NULL)
136             {
137                 /* It should match our service name */
138                 service_ok(wcscmp(lpszArgv[0], ServiceQuery.Buffer) == 0, "Mismatching info: %S - %S\n", lpszArgv[0], ServiceQuery.Buffer);
139                 service_ok(ServiceQuery.TagType == 1, "Invalid tag type: %x\n", ServiceQuery.TagType);
140                 LocalFree(ServiceQuery.Buffer);
141             }
142         }
143     }
144 
145     /* Work is done */
146     report_service_status(SERVICE_STOPPED, NO_ERROR, 0);
147 }
148 
149 static BOOL start_service(PCSTR service_nameA, PCWSTR service_nameW)
150 {
151     BOOL res;
152 
153     SERVICE_TABLE_ENTRYW servtbl[] =
154     {
155         { (PWSTR)service_nameW, service_main },
156         { NULL, NULL }
157     };
158 
159     res = StartServiceCtrlDispatcherW(servtbl);
160     service_ok(res, "StartServiceCtrlDispatcherW failed: %lu\n", GetLastError());
161     return res;
162 }
163 
164 
165 /*** Tester part of the test ***/
166 
167 static void
168 my_test_server(PCSTR service_nameA,
169                PCWSTR service_nameW,
170                void *param)
171 {
172     BOOL res;
173     SC_HANDLE hSC = NULL;
174     SC_HANDLE hService = NULL;
175     SERVICE_STATUS ServiceStatus;
176 
177     /* Open the SCM */
178     hSC = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
179     if (!hSC)
180     {
181         skip("OpenSCManagerW failed with error %lu!\n", GetLastError());
182         return;
183     }
184 
185     /* First create ourselves as a service running in the default LocalSystem account */
186     hService = register_service_exW(hSC, L"ServiceEnv", service_nameW, NULL,
187                                     SERVICE_ALL_ACCESS,
188                                     SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
189                                     SERVICE_DEMAND_START,
190                                     SERVICE_ERROR_IGNORE,
191                                     NULL, NULL, NULL,
192                                     NULL, NULL);
193     if (!hService)
194     {
195         skip("CreateServiceW failed with error %lu!\n", GetLastError());
196         goto Cleanup;
197     }
198 
199     /* Start it */
200     if (!StartServiceW(hService, 0, NULL))
201     {
202         skip("StartServiceW failed with error %lu!\n", GetLastError());
203         goto Cleanup;
204     }
205 
206     /* Wait for the service to stop by itself */
207     do
208     {
209         Sleep(100);
210         ZeroMemory(&ServiceStatus, sizeof(ServiceStatus));
211         res = QueryServiceStatus(hService, &ServiceStatus);
212     } while (res && ServiceStatus.dwCurrentState != SERVICE_STOPPED);
213     ok(res, "QueryServiceStatus failed: %lu\n", GetLastError());
214     ok(ServiceStatus.dwCurrentState == SERVICE_STOPPED, "ServiceStatus.dwCurrentState = %lx\n", ServiceStatus.dwCurrentState);
215 
216     /* Be sure the service is really stopped */
217     res = ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
218     if (!res && ServiceStatus.dwCurrentState != SERVICE_STOPPED &&
219         ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING &&
220         GetLastError() != ERROR_SERVICE_NOT_ACTIVE)
221     {
222         skip("ControlService failed with error %lu!\n", GetLastError());
223         goto Cleanup;
224     }
225 
226 #if 0
227     trace("Service stopped. Going to restart it...\n");
228 
229     /* Now change the service configuration to make it start under the NetworkService account */
230     if (!ChangeServiceConfigW(hService,
231                               SERVICE_NO_CHANGE,
232                               SERVICE_NO_CHANGE,
233                               SERVICE_NO_CHANGE,
234                               NULL, NULL, NULL, NULL,
235                               L"NT AUTHORITY\\NetworkService", L"",
236                               NULL))
237     {
238         skip("ChangeServiceConfigW failed with error %lu!\n", GetLastError());
239         goto Cleanup;
240     }
241 
242     /* Start it */
243     if (!StartServiceW(hService, 0, NULL))
244     {
245         skip("StartServiceW failed with error %lu!\n", GetLastError());
246         goto Cleanup;
247     }
248 
249     /* Wait for the service to stop by itself */
250     do
251     {
252         Sleep(100);
253         ZeroMemory(&ServiceStatus, sizeof(ServiceStatus));
254         res = QueryServiceStatus(hService, &ServiceStatus);
255     } while (res && ServiceStatus.dwCurrentState != SERVICE_STOPPED);
256     ok(res, "QueryServiceStatus failed: %lu\n", GetLastError());
257     ok(ServiceStatus.dwCurrentState == SERVICE_STOPPED, "ServiceStatus.dwCurrentState = %lx\n", ServiceStatus.dwCurrentState);
258 
259     /* Be sure the service is really stopped */
260     res = ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
261     if (!res && ServiceStatus.dwCurrentState != SERVICE_STOPPED &&
262         ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING &&
263         GetLastError() != ERROR_SERVICE_NOT_ACTIVE)
264     {
265         skip("ControlService failed with error %lu!\n", GetLastError());
266         goto Cleanup;
267     }
268 #endif
269 
270 Cleanup:
271     if (hService)
272     {
273         res = DeleteService(hService);
274         ok(res, "DeleteService failed: %lu\n", GetLastError());
275         CloseServiceHandle(hService);
276     }
277 
278     if (hSC)
279         CloseServiceHandle(hSC);
280 }
281 
282 START_TEST(ServiceEnv)
283 {
284     int argc;
285     char** argv;
286     PTEB Teb;
287 
288     /* Check whether this test is started as a separated service process */
289     argc = winetest_get_mainargs(&argv);
290     if (argc >= 3)
291     {
292         service_process(start_service, argc, argv);
293         return;
294     }
295 
296     Teb = NtCurrentTeb();
297     ok(Teb->SubProcessTag == 0, "SubProcessTag is defined: %p\n", Teb->SubProcessTag);
298 
299     /* We are started as the real test */
300     test_runner(my_test_server, NULL);
301     // trace("Returned from test_runner\n");
302 }
303