1 /* 2 * PROJECT: ReactOS api tests 3 * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory 4 * PURPOSE: Test for service arguments 5 * PROGRAMMER: Jacek Caban for CodeWeavers 6 * Thomas Faber <thomas.faber@reactos.org> 7 */ 8 9 #include "precomp.h" 10 11 static char **argv; 12 static int argc; 13 14 static HANDLE pipe_handle = INVALID_HANDLE_VALUE; 15 static char service_nameA[100]; 16 static WCHAR service_nameW[100]; 17 static WCHAR named_pipe_name[100]; 18 19 /* Test process global variables */ 20 static SC_HANDLE scm_handle; 21 static SERVICE_STATUS_HANDLE service_handle; 22 23 static void send_msg(const char *type, const char *msg) 24 { 25 DWORD written = 0; 26 char buf[512]; 27 28 StringCbPrintfA(buf, sizeof(buf), "%s:%s", type, msg); 29 WriteFile(pipe_handle, buf, lstrlenA(buf)+1, &written, NULL); 30 } 31 32 #if 0 33 static inline void service_trace(const char *msg, ...) 34 { 35 va_list valist; 36 char buf[512]; 37 38 va_start(valist, msg); 39 StringCbVPrintfA(buf, sizeof(buf), msg, valist); 40 va_end(valist); 41 42 send_msg("TRACE", buf); 43 } 44 #endif 45 46 static void service_ok(int cnd, const char *msg, ...) 47 { 48 va_list valist; 49 char buf[512]; 50 51 va_start(valist, msg); 52 StringCbVPrintfA(buf, sizeof(buf), msg, valist); 53 va_end(valist); 54 55 send_msg(cnd ? "OK" : "FAIL", buf); 56 } 57 58 static VOID WINAPI service_handler(DWORD ctrl) 59 { 60 SERVICE_STATUS status; 61 62 status.dwServiceType = SERVICE_WIN32; 63 status.dwControlsAccepted = SERVICE_ACCEPT_STOP; 64 status.dwWin32ExitCode = 0; 65 status.dwServiceSpecificExitCode = 0; 66 status.dwCheckPoint = 0; 67 status.dwWaitHint = 0; 68 69 switch(ctrl) 70 { 71 case SERVICE_CONTROL_STOP: 72 status.dwCurrentState = SERVICE_STOP_PENDING; 73 status.dwControlsAccepted = 0; 74 SetServiceStatus(service_handle, &status); 75 default: 76 status.dwCurrentState = SERVICE_RUNNING; 77 SetServiceStatus(service_handle, &status); 78 } 79 } 80 81 static void service_main_common(void) 82 { 83 SERVICE_STATUS status; 84 BOOL res; 85 86 service_handle = RegisterServiceCtrlHandlerW(service_nameW, service_handler); 87 service_ok(service_handle != NULL, "RegisterServiceCtrlHandler failed: %lu\n", GetLastError()); 88 if (!service_handle) 89 return; 90 91 status.dwServiceType = SERVICE_WIN32; 92 status.dwCurrentState = SERVICE_RUNNING; 93 status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; 94 status.dwWin32ExitCode = 0; 95 status.dwServiceSpecificExitCode = 0; 96 status.dwCheckPoint = 0; 97 status.dwWaitHint = 10000; 98 res = SetServiceStatus(service_handle, &status); 99 service_ok(res, "SetServiceStatus(SERVICE_RUNNING) failed: %lu", GetLastError()); 100 101 Sleep(100); 102 103 status.dwCurrentState = SERVICE_STOPPED; 104 status.dwControlsAccepted = 0; 105 res = SetServiceStatus(service_handle, &status); 106 service_ok(res, "SetServiceStatus(SERVICE_STOPPED) failed: %lu", GetLastError()); 107 } 108 109 /* 110 * A/W argument layout XP/2003: 111 * [argv array of pointers][parameter 1][parameter 2]...[service name] 112 * 113 * A/W argument layout Vista: 114 * [argv array of pointers][align to 8 bytes][parameter 1][parameter 2]...[service name] 115 * 116 * A/W argument layout Win7/8: 117 * [argv array of pointers][service name] 118 * [parameter 1][align to 4 bytes][parameter 2][align to 4 bytes]... 119 * 120 * Space for parameters and service name is always enough to store 121 * the WCHAR version including null terminator. 122 */ 123 124 static void WINAPI service_mainA(DWORD service_argc, char **service_argv) 125 { 126 int i; 127 char *next_arg; 128 char *next_arg_aligned; 129 130 service_ok(service_argc == argc - 3, "service_argc = %d, expected %d", service_argc, argc - 3); 131 if (service_argc == argc - 3) 132 { 133 service_ok(!strcmp(service_argv[0], service_nameA), "service_argv[0] = %s, expected %s", service_argv[0], service_nameA); 134 service_ok(service_argv[0] == (char *)&service_argv[service_argc] || 135 service_argv[1] == (char *)&service_argv[service_argc] || 136 service_argv[1] == (char *)(((ULONG_PTR)&service_argv[service_argc] + 7) & ~7), "service_argv[0] = %p, [1] = %p, expected one of them to be %p", service_argv[0], service_argv[1], &service_argv[service_argc]); 137 //service_trace("service_argv[0] = %p", service_argv[0]); 138 next_arg_aligned = next_arg = NULL; 139 for (i = 1; i < service_argc; i++) 140 { 141 //service_trace("service_argv[%d] = %p", i, service_argv[i]); 142 service_ok(!strcmp(service_argv[i], argv[i + 3]), "service_argv[%d] = %s, expected %s", i, service_argv[i], argv[i + 3]); 143 service_ok(next_arg == NULL || 144 service_argv[i] == next_arg || 145 service_argv[i] == next_arg_aligned, "service_argv[%d] = %p, expected %p or %p", i, service_argv[i], next_arg, next_arg_aligned); 146 next_arg = service_argv[i]; 147 next_arg += 2 * (strlen(next_arg) + 1); 148 next_arg_aligned = (char *)(((ULONG_PTR)next_arg + 3) & ~3); 149 } 150 } 151 152 service_main_common(); 153 } 154 155 static void WINAPI service_mainW(DWORD service_argc, WCHAR **service_argv) 156 { 157 int i; 158 WCHAR argW[32]; 159 WCHAR *next_arg; 160 WCHAR *next_arg_aligned; 161 162 service_ok(service_argc == argc - 3, "service_argc = %d, expected %d", service_argc, argc - 3); 163 if (service_argc == argc - 3) 164 { 165 service_ok(!wcscmp(service_argv[0], service_nameW), "service_argv[0] = %ls, expected %ls", service_argv[0], service_nameW); 166 service_ok(service_argv[0] == (WCHAR *)&service_argv[service_argc] || 167 service_argv[1] == (WCHAR *)&service_argv[service_argc] || 168 service_argv[1] == (WCHAR *)(((ULONG_PTR)&service_argv[service_argc] + 7) & ~7), "service_argv[0] = %p, [1] = %p, expected one of them to be %p", service_argv[0], service_argv[1], &service_argv[service_argc]); 169 //service_trace("service_argv[0] = %p", service_argv[0]); 170 next_arg_aligned = next_arg = NULL; 171 for (i = 1; i < service_argc; i++) 172 { 173 //service_trace("service_argv[%d] = %p", i, service_argv[i]); 174 MultiByteToWideChar(CP_ACP, 0, argv[i + 3], -1, argW, _countof(argW)); 175 service_ok(!wcscmp(service_argv[i], argW), "service_argv[%d] = %ls, expected %ls", i, service_argv[i], argW); 176 service_ok(next_arg == NULL || 177 service_argv[i] == next_arg || 178 service_argv[i] == next_arg_aligned, "service_argv[%d] = %p, expected %p or %p", i, service_argv[i], next_arg, next_arg_aligned); 179 next_arg = service_argv[i]; 180 next_arg += wcslen(next_arg) + 1; 181 next_arg_aligned = (WCHAR *)(((ULONG_PTR)next_arg + 3) & ~3); 182 } 183 } 184 185 service_main_common(); 186 } 187 188 static void service_process(BOOLEAN unicode) 189 { 190 BOOL res; 191 192 SERVICE_TABLE_ENTRYA servtblA[] = 193 { 194 { service_nameA, service_mainA }, 195 { NULL, NULL } 196 }; 197 SERVICE_TABLE_ENTRYW servtblW[] = 198 { 199 { service_nameW, service_mainW }, 200 { NULL, NULL } 201 }; 202 203 res = WaitNamedPipeW(named_pipe_name, NMPWAIT_USE_DEFAULT_WAIT); 204 if (!res) 205 return; 206 207 pipe_handle = CreateFileW(named_pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 208 if (pipe_handle == INVALID_HANDLE_VALUE) 209 return; 210 211 //service_trace("Starting..."); 212 if (unicode) 213 { 214 res = StartServiceCtrlDispatcherW(servtblW); 215 service_ok(res, "StartServiceCtrlDispatcherW failed: %lu\n", GetLastError()); 216 } 217 else 218 { 219 res = StartServiceCtrlDispatcherA(servtblA); 220 service_ok(res, "StartServiceCtrlDispatcherA failed: %lu\n", GetLastError()); 221 } 222 223 CloseHandle(pipe_handle); 224 } 225 226 static SC_HANDLE register_service(PCWSTR extra_args) 227 { 228 WCHAR service_cmd[MAX_PATH+150]; 229 SC_HANDLE service; 230 231 GetModuleFileNameW(NULL, service_cmd, MAX_PATH); 232 233 StringCbCatW(service_cmd, sizeof(service_cmd), L" ServiceArgs "); 234 StringCbCatW(service_cmd, sizeof(service_cmd), service_nameW); 235 StringCbCatW(service_cmd, sizeof(service_cmd), extra_args); 236 237 trace("service_cmd \"%ls\"\n", service_cmd); 238 239 service = CreateServiceW(scm_handle, service_nameW, service_nameW, SERVICE_ALL_ACCESS, 240 SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, 241 service_cmd, NULL, NULL, NULL, NULL, NULL); 242 if (!service && GetLastError() == ERROR_ACCESS_DENIED) 243 { 244 skip("Not enough access right to create service\n"); 245 return NULL; 246 } 247 248 ok(service != NULL, "CreateService failed: %lu\n", GetLastError()); 249 return service; 250 } 251 252 static DWORD WINAPI pipe_thread(void *arg) 253 { 254 char buf[512]; 255 DWORD read; 256 BOOL res; 257 258 res = ConnectNamedPipe(pipe_handle, NULL); 259 ok(res || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe failed: %lu\n", GetLastError()); 260 261 while (1) 262 { 263 res = ReadFile(pipe_handle, buf, sizeof(buf), &read, NULL); 264 if (!res) 265 { 266 ok(GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_INVALID_HANDLE, 267 "ReadFile failed: %lu\n", GetLastError()); 268 break; 269 } 270 271 if (!strncmp(buf, "TRACE:", 6)) 272 { 273 trace("service trace: %s\n", buf+6); 274 } 275 else if (!strncmp(buf, "OK:", 3)) 276 { 277 ok(1, "service: %s\n", buf+3); 278 } 279 else if (!strncmp(buf, "FAIL:", 5)) 280 { 281 ok(0, "service: %s\n", buf+5); 282 } 283 else 284 { 285 ok(0, "malformed service message: %s\n", buf); 286 } 287 } 288 289 DisconnectNamedPipe(pipe_handle); 290 //trace("pipe disconnected\n"); 291 return 0; 292 } 293 294 static void test_startA(SC_HANDLE service_handle, int service_argc, const char **service_argv) 295 { 296 SERVICE_STATUS status; 297 BOOL res; 298 299 res = StartServiceA(service_handle, service_argc, service_argv); 300 ok(res, "StartService failed: %lu\n", GetLastError()); 301 if (!res) 302 return; 303 304 do 305 { 306 Sleep(100); 307 ZeroMemory(&status, sizeof(status)); 308 res = QueryServiceStatus(service_handle, &status); 309 } while (res && status.dwCurrentState != SERVICE_STOPPED); 310 ok(res, "QueryServiceStatus failed: %lu\n", GetLastError()); 311 ok(status.dwCurrentState == SERVICE_STOPPED, "status.dwCurrentState = %lx\n", status.dwCurrentState); 312 } 313 314 static void test_startW(SC_HANDLE service_handle, int service_argc, const WCHAR **service_argv) 315 { 316 SERVICE_STATUS status; 317 BOOL res; 318 319 res = StartServiceW(service_handle, service_argc, service_argv); 320 ok(res, "StartService failed: %lu\n", GetLastError()); 321 if (!res) 322 return; 323 324 do 325 { 326 Sleep(100); 327 ZeroMemory(&status, sizeof(status)); 328 res = QueryServiceStatus(service_handle, &status); 329 } while (res && status.dwCurrentState != SERVICE_STOPPED); 330 ok(res, "QueryServiceStatus failed: %lu\n", GetLastError()); 331 ok(status.dwCurrentState == SERVICE_STOPPED, "status.dwCurrentState = %lx\n", status.dwCurrentState); 332 } 333 334 static void test_runner(BOOLEAN unicode, PCWSTR extra_args, int service_argc, void *service_argv) 335 { 336 HANDLE thread; 337 SC_HANDLE service_handle; 338 BOOL res; 339 340 StringCbPrintfW(service_nameW, sizeof(service_nameW), L"WineTestService%lu", GetTickCount()); 341 WideCharToMultiByte(CP_ACP, 0, service_nameW, -1, service_nameA, _countof(service_nameA), NULL, NULL); 342 //trace("service_name: %ls\n", service_nameW); 343 StringCbPrintfW(named_pipe_name, sizeof(named_pipe_name), L"\\\\.\\pipe\\%ls_pipe", service_nameW); 344 345 pipe_handle = CreateNamedPipeW(named_pipe_name, PIPE_ACCESS_INBOUND, 346 PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT, 10, 2048, 2048, 10000, NULL); 347 ok(pipe_handle != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError()); 348 if (pipe_handle == INVALID_HANDLE_VALUE) 349 return; 350 351 thread = CreateThread(NULL, 0, pipe_thread, NULL, 0, NULL); 352 ok(thread != NULL, "CreateThread failed: %lu\n", GetLastError()); 353 if (!thread) 354 goto Quit; 355 356 service_handle = register_service(extra_args); 357 if (!service_handle) 358 goto Quit; 359 360 //trace("starting...\n"); 361 362 if (unicode) 363 test_startW(service_handle, service_argc, service_argv); 364 else 365 test_startA(service_handle, service_argc, service_argv); 366 367 res = DeleteService(service_handle); 368 ok(res, "DeleteService failed: %lu\n", GetLastError()); 369 370 CloseServiceHandle(service_handle); 371 372 ok(WaitForSingleObject(thread, 10000) == WAIT_OBJECT_0, "Timeout waiting for thread\n"); 373 374 Quit: 375 if (thread) 376 CloseHandle(thread); 377 378 if (pipe_handle != INVALID_HANDLE_VALUE) 379 CloseHandle(pipe_handle); 380 } 381 382 START_TEST(ServiceArgs) 383 { 384 argc = winetest_get_mainargs(&argv); 385 386 scm_handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); 387 ok(scm_handle != NULL, "OpenSCManager failed: %lu\n", GetLastError()); 388 if (!scm_handle) 389 { 390 skip("Failed to open service control manager. Skipping test\n"); 391 return; 392 } 393 394 if (argc < 3) 395 { 396 char *service_argvA[10]; 397 WCHAR *service_argvW[10]; 398 399 test_runner(FALSE, L" A", 0, NULL); 400 test_runner(FALSE, L" W", 0, NULL); 401 test_runner(TRUE, L" A", 0, NULL); 402 test_runner(TRUE, L" W", 0, NULL); 403 404 service_argvA[0] = "x"; 405 service_argvW[0] = L"x"; 406 test_runner(FALSE, L" A x", 1, service_argvA); 407 test_runner(FALSE, L" W x", 1, service_argvA); 408 test_runner(TRUE, L" A x", 1, service_argvW); 409 test_runner(TRUE, L" W x", 1, service_argvW); 410 411 service_argvA[1] = "y"; 412 service_argvW[1] = L"y"; 413 test_runner(FALSE, L" A x y", 2, service_argvA); 414 test_runner(FALSE, L" W x y", 2, service_argvA); 415 test_runner(TRUE, L" A x y", 2, service_argvW); 416 test_runner(TRUE, L" W x y", 2, service_argvW); 417 418 service_argvA[0] = "ab"; 419 service_argvW[0] = L"ab"; 420 test_runner(FALSE, L" A ab y", 2, service_argvA); 421 test_runner(FALSE, L" W ab y", 2, service_argvA); 422 test_runner(TRUE, L" A ab y", 2, service_argvW); 423 test_runner(TRUE, L" W ab y", 2, service_argvW); 424 425 service_argvA[0] = "abc"; 426 service_argvW[0] = L"abc"; 427 test_runner(FALSE, L" A abc y", 2, service_argvA); 428 test_runner(FALSE, L" W abc y", 2, service_argvA); 429 test_runner(TRUE, L" A abc y", 2, service_argvW); 430 test_runner(TRUE, L" W abc y", 2, service_argvW); 431 432 service_argvA[0] = "abcd"; 433 service_argvW[0] = L"abcd"; 434 test_runner(FALSE, L" A abcd y", 2, service_argvA); 435 test_runner(FALSE, L" W abcd y", 2, service_argvA); 436 test_runner(TRUE, L" A abcd y", 2, service_argvW); 437 test_runner(TRUE, L" W abcd y", 2, service_argvW); 438 439 service_argvA[0] = "abcde"; 440 service_argvW[0] = L"abcde"; 441 test_runner(FALSE, L" A abcde y", 2, service_argvA); 442 test_runner(FALSE, L" W abcde y", 2, service_argvA); 443 test_runner(TRUE, L" A abcde y", 2, service_argvW); 444 test_runner(TRUE, L" W abcde y", 2, service_argvW); 445 446 service_argvA[0] = "abcdef"; 447 service_argvW[0] = L"abcdef"; 448 test_runner(FALSE, L" A abcdef y", 2, service_argvA); 449 test_runner(FALSE, L" W abcdef y", 2, service_argvA); 450 test_runner(TRUE, L" A abcdef y", 2, service_argvW); 451 test_runner(TRUE, L" W abcdef y", 2, service_argvW); 452 453 service_argvA[0] = "abcdefg"; 454 service_argvW[0] = L"abcdefg"; 455 test_runner(FALSE, L" A abcdefg y", 2, service_argvA); 456 test_runner(FALSE, L" W abcdefg y", 2, service_argvA); 457 test_runner(TRUE, L" A abcdefg y", 2, service_argvW); 458 test_runner(TRUE, L" W abcdefg y", 2, service_argvW); 459 460 service_argvA[0] = ""; 461 service_argvW[0] = L""; 462 test_runner(FALSE, L" A \"\" y", 2, service_argvA); 463 test_runner(FALSE, L" W \"\" y", 2, service_argvA); 464 test_runner(TRUE, L" A \"\" y", 2, service_argvW); 465 test_runner(TRUE, L" W \"\" y", 2, service_argvW); 466 } 467 else 468 { 469 strcpy(service_nameA, argv[2]); 470 MultiByteToWideChar(CP_ACP, 0, service_nameA, -1, service_nameW, _countof(service_nameW)); 471 StringCbPrintfW(named_pipe_name, sizeof(named_pipe_name), L"\\\\.\\pipe\\%ls_pipe", service_nameW); 472 if (!strcmp(argv[3], "A")) 473 service_process(FALSE); 474 else 475 service_process(TRUE); 476 } 477 478 CloseServiceHandle(scm_handle); 479 } 480