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