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