2 * Copyright 2012 Jacek Caban for CodeWeavers
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.
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.
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
22 #include "wine/test.h"
24 static SERVICE_STATUS_HANDLE (WINAPI
*pRegisterServiceCtrlHandlerExA
)(LPCSTR
,LPHANDLER_FUNCTION_EX
,LPVOID
);
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
;
30 /* Service process global variables */
31 static HANDLE service_stop_event
;
33 static void send_msg(const char *type
, const char *msg
)
38 sprintf(buf
, "%s:%s", type
, msg
);
39 WriteFile(pipe_handle
, buf
, strlen(buf
)+1, &written
, NULL
);
42 static inline void service_trace(const char *msg
)
44 send_msg("TRACE", msg
);
47 static inline void service_event(const char *event
)
49 send_msg("EVENT", event
);
52 static void service_ok(int cnd
, const char *msg
, ...)
57 va_start(valist
, msg
);
58 vsprintf(buf
, msg
, valist
);
61 send_msg(cnd
? "OK" : "FAIL", buf
);
64 static DWORD WINAPI
service_handler(DWORD ctrl
, DWORD event_type
, void *event_data
, void *context
)
66 SERVICE_STATUS status
;
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;
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
);
86 service_event("CUSTOM");
89 status
.dwCurrentState
= SERVICE_RUNNING
;
90 SetServiceStatus( service_handle
, &status
);
95 static void WINAPI
service_main(DWORD argc
, char **argv
)
97 SERVICE_STATUS status
;
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]);
107 GetEnvironmentVariableA("PATHEXT", buf
, sizeof(buf
));
108 service_ok(buf
[0], "did not find PATHEXT environment variable\n");
110 service_handle
= pRegisterServiceCtrlHandlerExA(service_name
, service_handler
, NULL
);
111 service_ok(service_handle
!= NULL
, "RegisterServiceCtrlHandlerEx failed: %u\n", GetLastError());
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());
125 service_event("RUNNING");
127 WaitForSingleObject(service_stop_event
, INFINITE
);
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());
135 static void service_process(void (WINAPI
*p_service_main
)(DWORD
, char **))
139 SERVICE_TABLE_ENTRYA servtbl
[] = {
140 {service_name
, p_service_main
},
144 res
= WaitNamedPipeA(named_pipe_name
, NMPWAIT_USE_DEFAULT_WAIT
);
148 pipe_handle
= CreateFileA(named_pipe_name
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
149 if(pipe_handle
== INVALID_HANDLE_VALUE
)
152 service_trace("Starting...\n");
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
)
159 res
= StartServiceCtrlDispatcherA(servtbl
);
160 service_ok(res
, "StartServiceCtrlDispatcher failed: %u\n", GetLastError());
162 /* Let service thread terminate */
165 CloseHandle(service_stop_event
);
166 CloseHandle(pipe_handle
);
169 static DWORD WINAPI
no_stop_handler(DWORD ctrl
, DWORD event_type
, void *event_data
, void *context
)
171 SERVICE_STATUS status
;
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;
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
);
191 status
.dwCurrentState
= SERVICE_RUNNING
;
192 SetServiceStatus( service_handle
, &status
);
197 static void WINAPI
no_stop_main(DWORD argc
, char **argv
)
199 SERVICE_STATUS status
;
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
);
205 service_handle
= pRegisterServiceCtrlHandlerExA(service_name
, no_stop_handler
, NULL
);
206 service_ok(service_handle
!= NULL
, "RegisterServiceCtrlHandlerEx failed: %u\n", GetLastError());
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());
220 service_event("RUNNING");
223 /* Test process global variables */
224 static SC_HANDLE scm_handle
;
226 static char current_event
[32];
227 static HANDLE event_handle
= INVALID_HANDLE_VALUE
;
228 static CRITICAL_SECTION event_cs
;
230 static SC_HANDLE
register_service(const char *test_name
)
232 char service_cmd
[MAX_PATH
+150], *ptr
;
235 ptr
= service_cmd
+ GetModuleFileNameA(NULL
, service_cmd
, MAX_PATH
);
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
) {
243 strcpy(ptr
, " service ");
245 sprintf(ptr
, "%s ", test_name
);
247 strcpy(ptr
, service_name
);
249 trace("service_cmd \"%s\"\n", service_cmd
);
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");
259 ok(service
!= NULL
, "CreateService failed: %u\n", GetLastError());
263 static void expect_event(const char *event_name
)
268 trace("waiting for %s\n", event_name
);
270 res
= WaitForSingleObject(event_handle
, 30000);
271 ok(res
== WAIT_OBJECT_0
, "WaitForSingleObject failed: %u\n", res
);
272 if(res
!= WAIT_OBJECT_0
)
275 EnterCriticalSection(&event_cs
);
276 strcpy(evt
, current_event
);
278 LeaveCriticalSection(&event_cs
);
280 ok(!strcmp(evt
, event_name
), "Unexpected event: %s, expected %s\n", evt
, event_name
);
283 static DWORD WINAPI
pipe_thread(void *arg
)
289 res
= ConnectNamedPipe(pipe_handle
, NULL
);
290 ok(res
|| GetLastError() == ERROR_PIPE_CONNECTED
, "ConnectNamedPipe failed: %u\n", GetLastError());
293 res
= ReadFile(pipe_handle
, buf
, sizeof(buf
), &read
, NULL
);
295 ok(GetLastError() == ERROR_BROKEN_PIPE
|| GetLastError() == ERROR_INVALID_HANDLE
,
296 "ReadFile failed: %u\n", GetLastError());
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
);
315 ok(0, "malformed service message: %s\n", ptr
);
320 DisconnectNamedPipe(pipe_handle
);
321 trace("pipe disconnected\n");
325 static void test_service(void)
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
;
337 trace("starting...\n");
338 res
= StartServiceA(service_handle
, 2, argv
);
339 ok(res
, "StartService failed: %u\n", GetLastError());
341 DeleteService(service_handle
);
342 CloseServiceHandle(service_handle
);
345 expect_event("RUNNING");
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
);
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
);
364 res
= ControlService(service_handle
, 128, &status
);
365 ok(res
, "ControlService failed: %u\n", GetLastError());
366 expect_event("CUSTOM");
368 res
= ControlService(service_handle
, SERVICE_CONTROL_STOP
, &status
);
369 ok(res
, "ControlService failed: %u\n", GetLastError());
370 expect_event("STOP");
372 res
= DeleteService(service_handle
);
373 ok(res
, "DeleteService failed: %u\n", GetLastError());
375 CloseServiceHandle(service_handle
);
378 static inline void test_no_stop(void)
380 SC_HANDLE service_handle
= register_service("no_stop");
381 SERVICE_STATUS_PROCESS status2
;
382 SERVICE_STATUS status
;
389 trace("starting...\n");
390 res
= StartServiceA(service_handle
, 0, NULL
);
391 ok(res
, "StartService failed: %u\n", GetLastError());
393 DeleteService(service_handle
);
394 CloseServiceHandle(service_handle
);
397 expect_event("RUNNING");
399 /* Let service thread terminate */
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
);
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
);
419 res
= ControlService(service_handle
, SERVICE_CONTROL_STOP
, &status
);
420 ok(res
, "ControlService failed: %u\n", GetLastError());
421 expect_event("STOP");
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
);
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
);
440 res
= DeleteService(service_handle
);
441 ok(res
, "DeleteService failed: %u\n", GetLastError());
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
);
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
);
460 CloseServiceHandle(service_handle
);
462 res
= QueryServiceStatus(service_handle
, &status
);
463 ok(!res
, "QueryServiceStatus should have failed\n");
464 ok(GetLastError() == ERROR_INVALID_HANDLE
, "GetLastError = %d\n", GetLastError());
467 static void test_runner(void (*p_run_test
)(void))
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
);
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
)
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
)
486 thread
= CreateThread(NULL
, 0, pipe_thread
, NULL
, 0, NULL
);
487 ok(thread
!= NULL
, "CreateThread failed: %u\n", GetLastError());
493 WaitForSingleObject(thread
, INFINITE
);
494 CloseHandle(event_handle
);
495 CloseHandle(pipe_handle
);
503 InitializeCriticalSection(&event_cs
);
505 pRegisterServiceCtrlHandlerExA
= (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA");
506 if(!pRegisterServiceCtrlHandlerExA
) {
507 win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n");
511 scm_handle
= OpenSCManagerA(NULL
, NULL
, GENERIC_ALL
);
512 ok(scm_handle
!= NULL
|| GetLastError() == ERROR_ACCESS_DENIED
, "OpenSCManager failed: %u\n", GetLastError());
514 skip("OpenSCManager failed, skipping tests\n");
518 argc
= winetest_get_mainargs(&argv
);
521 test_runner(test_service
);
522 test_runner(test_no_stop
);
524 strcpy(service_name
, argv
[3]);
525 sprintf(named_pipe_name
, "\\\\.\\pipe\\%s_pipe", service_name
);
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
);
533 CloseServiceHandle(scm_handle
);