[ADVAPI32_APITEST]: Add a small helper lib 'svchlp' for common routines for test...
[reactos.git] / rostests / apitests / advapi32 / svchlp.c
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 }