[FORMATTING] Remove trailing whitespace. Addendum to 34593d93.
[reactos.git] / modules / 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 "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 }