1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Support helpers for embedded services inside api tests.
5  * PROGRAMMERS:     Jacek Caban for CodeWeavers
6  *                  Thomas Faber <thomas.faber@reactos.org>
7  *                  Hermes Belusca-Maito
8  *
9  * NOTE: Room for improvements:
10  * - One test_runner managing 1 pipe for 1 service process that is shared
11  *   by multiple services of type SERVICE_WIN32_SHARE_PROCESS.
12  * - Find a way to elegantly determine the registered service name inside
13  *   the service process, without really passing it
14  */
15 
16 #include <apitest.h>
17 #include <winnls.h>
18 #include <winsvc.h>
19 #include <strsafe.h>
20 
21 static HANDLE hClientPipe = INVALID_HANDLE_VALUE;
22 static WCHAR named_pipe_name[100]; // Shared: FIXME!
23 
24 static CHAR  service_nameA[100];
25 static WCHAR service_nameW[100];
26 
27 
28 /**********  S E R V I C E   ( C L I E N T )   M O D U L E   S I D E  *********/
29 
30 void send_msg(const char *type, const char *msg)
31 {
32     DWORD written = 0;
33     char buf[512];
34 
35     StringCbPrintfA(buf, sizeof(buf), "%s:%s", type, msg);
36     WriteFile(hClientPipe, buf, strlen(buf)+1, &written, NULL);
37 }
38 
39 void service_trace(const char *msg, ...)
40 {
41     va_list valist;
42     char buf[512];
43 
44     va_start(valist, msg);
45     StringCbVPrintfA(buf, sizeof(buf), msg, valist);
46     va_end(valist);
47 
48     send_msg("TRACE", buf);
49 }
50 
51 void service_ok(int cnd, const char *msg, ...)
52 {
53     va_list valist;
54     char buf[512];
55 
56     va_start(valist, msg);
57     StringCbVPrintfA(buf, sizeof(buf), msg, valist);
58     va_end(valist);
59 
60     send_msg(cnd ? "OK" : "FAIL", buf);
61 }
62 
63 void service_process(BOOL (*start_service)(PCSTR, PCWSTR), int argc, char** argv)
64 {
65     BOOL res;
66 
67     StringCbCopyA(service_nameA, sizeof(service_nameA), argv[2]);
68     MultiByteToWideChar(CP_ACP, 0, service_nameA, -1, service_nameW, _countof(service_nameW));
69     StringCbPrintfW(named_pipe_name, sizeof(named_pipe_name), L"\\\\.\\pipe\\%ls_pipe", service_nameW);
70 
71     res = WaitNamedPipeW(named_pipe_name, NMPWAIT_USE_DEFAULT_WAIT);
72     if (!res)
73         return;
74 
75     hClientPipe = CreateFileW(named_pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
76     if (hClientPipe == INVALID_HANDLE_VALUE)
77         return;
78 
79     service_trace("Service process starting...\n");
80     res = start_service(service_nameA, service_nameW);
81     service_trace("Service process stopped.\n");
82 
83     CloseHandle(hClientPipe);
84 }
85 
86 
87 /***********  T E S T E R   ( S E R V E R )   M O D U L E   S I D E  **********/
88 
89 SC_HANDLE register_service_exA(
90     SC_HANDLE scm_handle,
91     PCSTR test_name,
92     PCSTR service_name, // LPCSTR lpServiceName,
93     PCSTR extra_args OPTIONAL,
94     DWORD dwDesiredAccess,
95     DWORD dwServiceType,
96     DWORD dwStartType,
97     DWORD dwErrorControl,
98     LPCSTR lpLoadOrderGroup OPTIONAL,
99     LPDWORD lpdwTagId OPTIONAL,
100     LPCSTR lpDependencies OPTIONAL,
101     LPCSTR lpServiceStartName OPTIONAL,
102     LPCSTR lpPassword OPTIONAL)
103 {
104     SC_HANDLE service;
105     CHAR service_cmd[MAX_PATH+150];
106 
107     /* Retrieve our full path */
108     if (!GetModuleFileNameA(NULL, service_cmd, MAX_PATH))
109     {
110         skip("GetModuleFileNameW failed with error %lu!\n", GetLastError());
111         return NULL;
112     }
113 
114     /*
115      * Build up our custom command line. The first parameter is the test name,
116      * the second parameter is the flag used to decide whether we should start
117      * as a service.
118      */
119     StringCbCatA(service_cmd, sizeof(service_cmd), " ");
120     StringCbCatA(service_cmd, sizeof(service_cmd), test_name);
121     StringCbCatA(service_cmd, sizeof(service_cmd), " ");
122     StringCbCatA(service_cmd, sizeof(service_cmd), service_name);
123     if (extra_args)
124     {
125         StringCbCatA(service_cmd, sizeof(service_cmd), " ");
126         StringCbCatA(service_cmd, sizeof(service_cmd), extra_args);
127     }
128 
129     trace("service_cmd \"%s\"\n", service_cmd);
130 
131     service = CreateServiceA(scm_handle, service_name, service_name,
132                              dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
133                              service_cmd, lpLoadOrderGroup, lpdwTagId, lpDependencies,
134                              lpServiceStartName, lpPassword);
135     if (!service && GetLastError() == ERROR_ACCESS_DENIED)
136     {
137         skip("Not enough access right to create service.\n");
138         return NULL;
139     }
140 
141     ok(service != NULL, "CreateService failed: %lu\n", GetLastError());
142     return service;
143 }
144 
145 SC_HANDLE register_service_exW(
146     SC_HANDLE scm_handle,
147     PCWSTR test_name,
148     PCWSTR service_name, // LPCWSTR lpServiceName,
149     PCWSTR extra_args OPTIONAL,
150     DWORD dwDesiredAccess,
151     DWORD dwServiceType,
152     DWORD dwStartType,
153     DWORD dwErrorControl,
154     LPCWSTR lpLoadOrderGroup OPTIONAL,
155     LPDWORD lpdwTagId OPTIONAL,
156     LPCWSTR lpDependencies OPTIONAL,
157     LPCWSTR lpServiceStartName OPTIONAL,
158     LPCWSTR lpPassword OPTIONAL)
159 {
160     SC_HANDLE service;
161     WCHAR service_cmd[MAX_PATH+150];
162 
163     /* Retrieve our full path */
164     if (!GetModuleFileNameW(NULL, service_cmd, MAX_PATH))
165     {
166         skip("GetModuleFileNameW failed with error %lu!\n", GetLastError());
167         return NULL;
168     }
169 
170     /*
171      * Build up our custom command line. The first parameter is the test name,
172      * the second parameter is the flag used to decide whether we should start
173      * as a service.
174      */
175     StringCbCatW(service_cmd, sizeof(service_cmd), L" ");
176     StringCbCatW(service_cmd, sizeof(service_cmd), test_name);
177     StringCbCatW(service_cmd, sizeof(service_cmd), L" ");
178     StringCbCatW(service_cmd, sizeof(service_cmd), service_name);
179     if (extra_args)
180     {
181         StringCbCatW(service_cmd, sizeof(service_cmd), L" ");
182         StringCbCatW(service_cmd, sizeof(service_cmd), extra_args);
183     }
184 
185     trace("service_cmd \"%ls\"\n", service_cmd);
186 
187     service = CreateServiceW(scm_handle, service_name, service_name,
188                              dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
189                              service_cmd, lpLoadOrderGroup, lpdwTagId, lpDependencies,
190                              lpServiceStartName, lpPassword);
191     if (!service && GetLastError() == ERROR_ACCESS_DENIED)
192     {
193         skip("Not enough access right to create service.\n");
194         return NULL;
195     }
196 
197     ok(service != NULL, "CreateService failed: %lu\n", GetLastError());
198     return service;
199 }
200 
201 SC_HANDLE register_serviceA(
202     SC_HANDLE scm_handle,
203     PCSTR test_name,
204     PCSTR service_name,
205     PCSTR extra_args OPTIONAL)
206 {
207     return register_service_exA(scm_handle, test_name, service_name, extra_args,
208                                 SERVICE_ALL_ACCESS,
209                                 SERVICE_WIN32_OWN_PROCESS,
210                                 SERVICE_DEMAND_START,
211                                 SERVICE_ERROR_IGNORE,
212                                 NULL, NULL, NULL, NULL, NULL);
213 }
214 
215 SC_HANDLE register_serviceW(
216     SC_HANDLE scm_handle,
217     PCWSTR test_name,
218     PCWSTR service_name,
219     PCWSTR extra_args OPTIONAL)
220 {
221     return register_service_exW(scm_handle, test_name, service_name, extra_args,
222                                 SERVICE_ALL_ACCESS,
223                                 SERVICE_WIN32_OWN_PROCESS,
224                                 SERVICE_DEMAND_START,
225                                 SERVICE_ERROR_IGNORE,
226                                 NULL, NULL, NULL, NULL, NULL);
227 }
228 
229 static DWORD WINAPI pipe_thread(void *param)
230 {
231     HANDLE hServerPipe = (HANDLE)param;
232     DWORD read;
233     BOOL res;
234     char buf[512];
235 
236     // printf("pipe_thread -- ConnectNamedPipe...\n");
237     res = ConnectNamedPipe(hServerPipe, NULL);
238     ok(res || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe failed: %lu\n", GetLastError());
239     // printf("pipe_thread -- ConnectNamedPipe ok\n");
240 
241     while (1)
242     {
243         res = ReadFile(hServerPipe, buf, sizeof(buf), &read, NULL);
244         if (!res)
245         {
246             ok(GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_INVALID_HANDLE,
247                "ReadFile failed: %lu\n", GetLastError());
248             // printf("pipe_thread -- break loop\n");
249             break;
250         }
251 
252         if (!strncmp(buf, "TRACE:", 6))
253         {
254             trace("service trace: %s", buf+6);
255         }
256         else if (!strncmp(buf, "OK:", 3))
257         {
258             ok(1, "service: %s", buf+3);
259         }
260         else if (!strncmp(buf, "FAIL:", 5))
261         {
262             ok(0, "service: %s", buf+5);
263         }
264         else
265         {
266             ok(0, "malformed service message: %s\n", buf);
267         }
268     }
269 
270     // printf("pipe_thread -- DisconnectNamedPipe\n");
271 
272     /*
273      * Flush the pipe to allow the client to read
274      * the pipe's contents before disconnecting.
275      */
276     FlushFileBuffers(hServerPipe);
277 
278     DisconnectNamedPipe(hServerPipe);
279     trace("pipe disconnected\n");
280     return 0;
281 }
282 
283 void test_runner(void (*run_test)(PCSTR, PCWSTR, void*), void *param)
284 {
285     HANDLE hServerPipe = INVALID_HANDLE_VALUE;
286     HANDLE hThread;
287 
288     StringCbPrintfW(service_nameW, sizeof(service_nameW), L"WineTestService%lu", GetTickCount());
289     WideCharToMultiByte(CP_ACP, 0, service_nameW, -1, service_nameA, _countof(service_nameA), NULL, NULL);
290     //trace("service_name: %ls\n", service_nameW);
291     StringCbPrintfW(named_pipe_name, sizeof(named_pipe_name), L"\\\\.\\pipe\\%ls_pipe", service_nameW);
292 
293     hServerPipe = CreateNamedPipeW(named_pipe_name, PIPE_ACCESS_INBOUND,
294                                    PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT, 10, 2048, 2048, 10000, NULL);
295     ok(hServerPipe != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError());
296     if (hServerPipe == INVALID_HANDLE_VALUE)
297         return;
298 
299     hThread = CreateThread(NULL, 0, pipe_thread, (LPVOID)hServerPipe, 0, NULL);
300     ok(hThread != NULL, "CreateThread failed: %lu\n", GetLastError());
301     if (!hThread)
302         goto Quit;
303 
304     run_test(service_nameA, service_nameW, param);
305 
306     ok(WaitForSingleObject(hThread, 10000) == WAIT_OBJECT_0, "Timeout waiting for thread\n");
307 
308 Quit:
309     if (hThread)
310     {
311         /* Be sure to kill the thread if it did not already terminate */
312         TerminateThread(hThread, 0);
313         CloseHandle(hThread);
314     }
315 
316     if (hServerPipe != INVALID_HANDLE_VALUE)
317         CloseHandle(hServerPipe);
318 }
319