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