[SERVICES_WINETEST] Sync with Wine Staging 1.9.18.
[reactos.git] / rostests / winetests / services / service.c
1 /*
2 * Copyright 2012 Jacek Caban for CodeWeavers
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include <windows.h>
20 #include <stdio.h>
21
22 #include "wine/test.h"
23
24 static SERVICE_STATUS_HANDLE (WINAPI *pRegisterServiceCtrlHandlerExA)(LPCSTR,LPHANDLER_FUNCTION_EX,LPVOID);
25
26 static HANDLE pipe_handle = INVALID_HANDLE_VALUE;
27 static char service_name[100], named_pipe_name[100];
28 static SERVICE_STATUS_HANDLE service_handle;
29
30 /* Service process global variables */
31 static HANDLE service_stop_event;
32
33 static void send_msg(const char *type, const char *msg)
34 {
35 DWORD written = 0;
36 char buf[512];
37
38 sprintf(buf, "%s:%s", type, msg);
39 WriteFile(pipe_handle, buf, strlen(buf)+1, &written, NULL);
40 }
41
42 static inline void service_trace(const char *msg)
43 {
44 send_msg("TRACE", msg);
45 }
46
47 static inline void service_event(const char *event)
48 {
49 send_msg("EVENT", event);
50 }
51
52 static void service_ok(int cnd, const char *msg, ...)
53 {
54 va_list valist;
55 char buf[512];
56
57 va_start(valist, msg);
58 vsprintf(buf, msg, valist);
59 va_end(valist);
60
61 send_msg(cnd ? "OK" : "FAIL", buf);
62 }
63
64 static DWORD WINAPI service_handler(DWORD ctrl, DWORD event_type, void *event_data, void *context)
65 {
66 SERVICE_STATUS status;
67
68 status.dwServiceType = SERVICE_WIN32;
69 status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
70 status.dwWin32ExitCode = 0;
71 status.dwServiceSpecificExitCode = 0;
72 status.dwCheckPoint = 0;
73 status.dwWaitHint = 0;
74
75 switch(ctrl)
76 {
77 case SERVICE_CONTROL_STOP:
78 case SERVICE_CONTROL_SHUTDOWN:
79 service_event("STOP");
80 status.dwCurrentState = SERVICE_STOP_PENDING;
81 status.dwControlsAccepted = 0;
82 SetServiceStatus(service_handle, &status);
83 SetEvent(service_stop_event);
84 return NO_ERROR;
85 case 128:
86 service_event("CUSTOM");
87 return 0xdeadbeef;
88 default:
89 status.dwCurrentState = SERVICE_RUNNING;
90 SetServiceStatus( service_handle, &status );
91 return NO_ERROR;
92 }
93 }
94
95 static void WINAPI service_main(DWORD argc, char **argv)
96 {
97 SERVICE_STATUS status;
98 char buf[64];
99 BOOL res;
100
101 service_ok(argc == 3, "argc = %u, expected 3\n", argc);
102 service_ok(!strcmp(argv[0], service_name), "argv[0] = '%s', expected '%s'\n", argv[0], service_name);
103 service_ok(!strcmp(argv[1], "param1"), "argv[1] = '%s', expected 'param1'\n", argv[1]);
104 service_ok(!strcmp(argv[2], "param2"), "argv[2] = '%s', expected 'param2'\n", argv[2]);
105
106 buf[0] = 0;
107 GetEnvironmentVariableA("PATHEXT", buf, sizeof(buf));
108 service_ok(buf[0], "did not find PATHEXT environment variable\n");
109
110 service_handle = pRegisterServiceCtrlHandlerExA(service_name, service_handler, NULL);
111 service_ok(service_handle != NULL, "RegisterServiceCtrlHandlerEx failed: %u\n", GetLastError());
112 if(!service_handle)
113 return;
114
115 status.dwServiceType = SERVICE_WIN32;
116 status.dwCurrentState = SERVICE_RUNNING;
117 status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
118 status.dwWin32ExitCode = 0;
119 status.dwServiceSpecificExitCode = 0;
120 status.dwCheckPoint = 0;
121 status.dwWaitHint = 10000;
122 res = SetServiceStatus(service_handle, &status);
123 service_ok(res, "SetServiceStatus(SERVICE_RUNNING) failed: %u\n", GetLastError());
124
125 service_event("RUNNING");
126
127 WaitForSingleObject(service_stop_event, INFINITE);
128
129 status.dwCurrentState = SERVICE_STOPPED;
130 status.dwControlsAccepted = 0;
131 res = SetServiceStatus(service_handle, &status);
132 service_ok(res, "SetServiceStatus(SERVICE_STOPPED) failed: %u\n", GetLastError());
133 }
134
135 static void service_process(void (WINAPI *p_service_main)(DWORD, char **))
136 {
137 BOOL res;
138
139 SERVICE_TABLE_ENTRYA servtbl[] = {
140 {service_name, p_service_main},
141 {NULL, NULL}
142 };
143
144 res = WaitNamedPipeA(named_pipe_name, NMPWAIT_USE_DEFAULT_WAIT);
145 if(!res)
146 return;
147
148 pipe_handle = CreateFileA(named_pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
149 if(pipe_handle == INVALID_HANDLE_VALUE)
150 return;
151
152 service_trace("Starting...\n");
153
154 service_stop_event = CreateEventA(NULL, TRUE, FALSE, NULL);
155 service_ok(service_stop_event != NULL, "Could not create event: %u\n", GetLastError());
156 if(!service_stop_event)
157 return;
158
159 res = StartServiceCtrlDispatcherA(servtbl);
160 service_ok(res, "StartServiceCtrlDispatcher failed: %u\n", GetLastError());
161
162 /* Let service thread terminate */
163 Sleep(50);
164
165 CloseHandle(service_stop_event);
166 CloseHandle(pipe_handle);
167 }
168
169 static DWORD WINAPI no_stop_handler(DWORD ctrl, DWORD event_type, void *event_data, void *context)
170 {
171 SERVICE_STATUS status;
172
173 status.dwServiceType = SERVICE_WIN32;
174 status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
175 status.dwWin32ExitCode = 0;
176 status.dwServiceSpecificExitCode = 0;
177 status.dwCheckPoint = 0;
178 status.dwWaitHint = 0;
179
180 switch(ctrl)
181 {
182 case SERVICE_CONTROL_STOP:
183 case SERVICE_CONTROL_SHUTDOWN:
184 service_event("STOP");
185 status.dwCurrentState = SERVICE_STOPPED;
186 status.dwControlsAccepted = 0;
187 SetServiceStatus(service_handle, &status);
188 SetEvent(service_stop_event);
189 return NO_ERROR;
190 default:
191 status.dwCurrentState = SERVICE_RUNNING;
192 SetServiceStatus( service_handle, &status );
193 return NO_ERROR;
194 }
195 }
196
197 static void WINAPI no_stop_main(DWORD argc, char **argv)
198 {
199 SERVICE_STATUS status;
200 BOOL res;
201
202 service_ok(argc == 1, "argc = %u, expected 1\n", argc);
203 service_ok(!strcmp(argv[0], service_name), "argv[0] = '%s', expected '%s'\n", argv[0], service_name);
204
205 service_handle = pRegisterServiceCtrlHandlerExA(service_name, no_stop_handler, NULL);
206 service_ok(service_handle != NULL, "RegisterServiceCtrlHandlerEx failed: %u\n", GetLastError());
207 if(!service_handle)
208 return;
209
210 status.dwServiceType = SERVICE_WIN32;
211 status.dwCurrentState = SERVICE_RUNNING;
212 status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
213 status.dwWin32ExitCode = 0;
214 status.dwServiceSpecificExitCode = 0;
215 status.dwCheckPoint = 0;
216 status.dwWaitHint = 10000;
217 res = SetServiceStatus(service_handle, &status);
218 service_ok(res, "SetServiceStatus(SERVICE_RUNNING) failed: %u\n", GetLastError());
219
220 service_event("RUNNING");
221 }
222
223 /* Test process global variables */
224 static SC_HANDLE scm_handle;
225
226 static char current_event[32];
227 static HANDLE event_handle = INVALID_HANDLE_VALUE;
228 static CRITICAL_SECTION event_cs;
229
230 static SC_HANDLE register_service(const char *test_name)
231 {
232 char service_cmd[MAX_PATH+150], *ptr;
233 SC_HANDLE service;
234
235 ptr = service_cmd + GetModuleFileNameA(NULL, service_cmd, MAX_PATH);
236
237 /* If the file doesn't exist, assume we're using built-in exe and append .so to the path */
238 if(GetFileAttributesA(service_cmd) == INVALID_FILE_ATTRIBUTES) {
239 strcpy(ptr, ".so");
240 ptr += 3;
241 }
242
243 strcpy(ptr, " service ");
244 ptr += strlen(ptr);
245 sprintf(ptr, "%s ", test_name);
246 ptr += strlen(ptr);
247 strcpy(ptr, service_name);
248
249 trace("service_cmd \"%s\"\n", service_cmd);
250
251 service = CreateServiceA(scm_handle, service_name, service_name, GENERIC_ALL,
252 SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE,
253 service_cmd, NULL, NULL, NULL, NULL, NULL);
254 if(!service && GetLastError() == ERROR_ACCESS_DENIED) {
255 skip("Not enough access right to create service\n");
256 return NULL;
257 }
258
259 ok(service != NULL, "CreateService failed: %u\n", GetLastError());
260 return service;
261 }
262
263 static void expect_event(const char *event_name)
264 {
265 char evt[32];
266 DWORD res;
267
268 trace("waiting for %s\n", event_name);
269
270 res = WaitForSingleObject(event_handle, 30000);
271 ok(res == WAIT_OBJECT_0, "WaitForSingleObject failed: %u\n", res);
272 if(res != WAIT_OBJECT_0)
273 return;
274
275 EnterCriticalSection(&event_cs);
276 strcpy(evt, current_event);
277 *current_event = 0;
278 LeaveCriticalSection(&event_cs);
279
280 ok(!strcmp(evt, event_name), "Unexpected event: %s, expected %s\n", evt, event_name);
281 }
282
283 static DWORD WINAPI pipe_thread(void *arg)
284 {
285 char buf[512], *ptr;
286 DWORD read;
287 BOOL res;
288
289 res = ConnectNamedPipe(pipe_handle, NULL);
290 ok(res || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe failed: %u\n", GetLastError());
291
292 while(1) {
293 res = ReadFile(pipe_handle, buf, sizeof(buf), &read, NULL);
294 if(!res) {
295 ok(GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_INVALID_HANDLE,
296 "ReadFile failed: %u\n", GetLastError());
297 break;
298 }
299
300 for(ptr = buf; ptr < buf+read; ptr += strlen(ptr)+1) {
301 if(!strncmp(ptr, "TRACE:", 6)) {
302 trace("service trace: %s", ptr+6);
303 }else if(!strncmp(ptr, "OK:", 3)) {
304 ok(1, "service: %s", ptr+3);
305 }else if(!strncmp(ptr, "FAIL:", 5)) {
306 ok(0, "service: %s", ptr+5);
307 }else if(!strncmp(ptr, "EVENT:", 6)) {
308 trace("service event: %s\n", ptr+6);
309 EnterCriticalSection(&event_cs);
310 ok(!current_event[0], "event %s still queued\n", current_event);
311 strcpy(current_event, ptr+6);
312 LeaveCriticalSection(&event_cs);
313 SetEvent(event_handle);
314 }else {
315 ok(0, "malformed service message: %s\n", ptr);
316 }
317 }
318 }
319
320 DisconnectNamedPipe(pipe_handle);
321 trace("pipe disconnected\n");
322 return 0;
323 }
324
325 static void test_service(void)
326 {
327 static const char *argv[2] = {"param1", "param2"};
328 SC_HANDLE service_handle = register_service("simple_service");
329 SERVICE_STATUS_PROCESS status2;
330 SERVICE_STATUS status;
331 DWORD bytes;
332 BOOL res;
333
334 if(!service_handle)
335 return;
336
337 trace("starting...\n");
338 res = StartServiceA(service_handle, 2, argv);
339 ok(res, "StartService failed: %u\n", GetLastError());
340 if(!res) {
341 DeleteService(service_handle);
342 CloseServiceHandle(service_handle);
343 return;
344 }
345 expect_event("RUNNING");
346
347 res = QueryServiceStatus(service_handle, &status);
348 ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
349 todo_wine ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
350 ok(status.dwCurrentState == SERVICE_RUNNING, "status.dwCurrentState = %x\n", status.dwCurrentState);
351 ok(status.dwControlsAccepted == (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN),
352 "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
353 ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
354 ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
355 status.dwServiceSpecificExitCode);
356 ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
357 todo_wine ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
358
359 res = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (BYTE *)&status2, sizeof(status2), &bytes);
360 ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError());
361 ok(status2.dwCurrentState == SERVICE_RUNNING, "status2.dwCurrentState = %x\n", status2.dwCurrentState);
362 ok(status2.dwProcessId != 0, "status2.dwProcessId = %d\n", status2.dwProcessId);
363
364 res = ControlService(service_handle, 128, &status);
365 ok(res, "ControlService failed: %u\n", GetLastError());
366 expect_event("CUSTOM");
367
368 res = ControlService(service_handle, SERVICE_CONTROL_STOP, &status);
369 ok(res, "ControlService failed: %u\n", GetLastError());
370 expect_event("STOP");
371
372 res = DeleteService(service_handle);
373 ok(res, "DeleteService failed: %u\n", GetLastError());
374
375 CloseServiceHandle(service_handle);
376 }
377
378 static inline void test_no_stop(void)
379 {
380 SC_HANDLE service_handle = register_service("no_stop");
381 SERVICE_STATUS_PROCESS status2;
382 SERVICE_STATUS status;
383 DWORD bytes;
384 BOOL res;
385
386 if(!service_handle)
387 return;
388
389 trace("starting...\n");
390 res = StartServiceA(service_handle, 0, NULL);
391 ok(res, "StartService failed: %u\n", GetLastError());
392 if(!res) {
393 DeleteService(service_handle);
394 CloseServiceHandle(service_handle);
395 return;
396 }
397 expect_event("RUNNING");
398
399 /* Let service thread terminate */
400 Sleep(1000);
401
402 res = QueryServiceStatus(service_handle, &status);
403 ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
404 todo_wine ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
405 ok(status.dwCurrentState == SERVICE_RUNNING, "status.dwCurrentState = %x\n", status.dwCurrentState);
406 ok(status.dwControlsAccepted == (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN),
407 "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
408 ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
409 ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
410 status.dwServiceSpecificExitCode);
411 ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
412 todo_wine ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
413
414 res = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (BYTE *)&status2, sizeof(status2), &bytes);
415 ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError());
416 ok(status2.dwCurrentState == SERVICE_RUNNING, "status2.dwCurrentState = %x\n", status2.dwCurrentState);
417 ok(status2.dwProcessId != 0, "status2.dwProcessId = %d\n", status2.dwProcessId);
418
419 res = ControlService(service_handle, SERVICE_CONTROL_STOP, &status);
420 ok(res, "ControlService failed: %u\n", GetLastError());
421 expect_event("STOP");
422
423 res = QueryServiceStatus(service_handle, &status);
424 ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
425 todo_wine ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
426 ok(status.dwCurrentState==SERVICE_STOPPED || status.dwCurrentState==SERVICE_STOP_PENDING,
427 "status.dwCurrentState = %x\n", status.dwCurrentState);
428 ok(status.dwControlsAccepted == 0, "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
429 ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
430 ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
431 status.dwServiceSpecificExitCode);
432 ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
433 ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
434
435 res = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (BYTE *)&status2, sizeof(status2), &bytes);
436 ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError());
437 ok(status2.dwProcessId == 0 || broken(status2.dwProcessId != 0),
438 "status2.dwProcessId = %d\n", status2.dwProcessId);
439
440 res = DeleteService(service_handle);
441 ok(res, "DeleteService failed: %u\n", GetLastError());
442
443 res = QueryServiceStatus(service_handle, &status);
444 ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
445 todo_wine ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
446 ok(status.dwCurrentState==SERVICE_STOPPED || status.dwCurrentState==SERVICE_STOP_PENDING,
447 "status.dwCurrentState = %x\n", status.dwCurrentState);
448 ok(status.dwControlsAccepted == 0, "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
449 ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
450 ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
451 status.dwServiceSpecificExitCode);
452 ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
453 ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
454
455 res = QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, (BYTE *)&status2, sizeof(status2), &bytes);
456 ok(res, "QueryServiceStatusEx failed: %u\n", GetLastError());
457 ok(status2.dwProcessId == 0 || broken(status2.dwProcessId != 0),
458 "status2.dwProcessId = %d\n", status2.dwProcessId);
459
460 CloseServiceHandle(service_handle);
461
462 res = QueryServiceStatus(service_handle, &status);
463 ok(!res, "QueryServiceStatus should have failed\n");
464 ok(GetLastError() == ERROR_INVALID_HANDLE, "GetLastError = %d\n", GetLastError());
465 }
466
467 static void test_runner(void (*p_run_test)(void))
468 {
469 HANDLE thread;
470
471 sprintf(service_name, "WineTestService%d", GetTickCount());
472 trace("service_name: %s\n", service_name);
473 sprintf(named_pipe_name, "\\\\.\\pipe\\%s_pipe", service_name);
474
475 pipe_handle = CreateNamedPipeA(named_pipe_name, PIPE_ACCESS_INBOUND,
476 PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, 10, 2048, 2048, 10000, NULL);
477 ok(pipe_handle != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %u\n", GetLastError());
478 if(pipe_handle == INVALID_HANDLE_VALUE)
479 return;
480
481 event_handle = CreateEventA(NULL, FALSE, FALSE, NULL);
482 ok(event_handle != INVALID_HANDLE_VALUE, "CreateEvent failed: %u\n", GetLastError());
483 if(event_handle == INVALID_HANDLE_VALUE)
484 return;
485
486 thread = CreateThread(NULL, 0, pipe_thread, NULL, 0, NULL);
487 ok(thread != NULL, "CreateThread failed: %u\n", GetLastError());
488 if(!thread)
489 return;
490
491 p_run_test();
492
493 WaitForSingleObject(thread, INFINITE);
494 CloseHandle(event_handle);
495 CloseHandle(pipe_handle);
496 }
497
498 START_TEST(service)
499 {
500 char **argv;
501 int argc;
502
503 InitializeCriticalSection(&event_cs);
504
505 pRegisterServiceCtrlHandlerExA = (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA");
506 if(!pRegisterServiceCtrlHandlerExA) {
507 win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n");
508 return;
509 }
510
511 scm_handle = OpenSCManagerA(NULL, NULL, GENERIC_ALL);
512 ok(scm_handle != NULL || GetLastError() == ERROR_ACCESS_DENIED, "OpenSCManager failed: %u\n", GetLastError());
513 if(!scm_handle) {
514 skip("OpenSCManager failed, skipping tests\n");
515 return;
516 }
517
518 argc = winetest_get_mainargs(&argv);
519
520 if(argc < 3) {
521 test_runner(test_service);
522 test_runner(test_no_stop);
523 }else {
524 strcpy(service_name, argv[3]);
525 sprintf(named_pipe_name, "\\\\.\\pipe\\%s_pipe", service_name);
526
527 if(!strcmp(argv[2], "simple_service"))
528 service_process(service_main);
529 else if(!strcmp(argv[2], "no_stop"))
530 service_process(no_stop_main);
531 }
532
533 CloseServiceHandle(scm_handle);
534 }