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