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
23 #include "wine/test.h"
25 static SERVICE_STATUS_HANDLE (WINAPI
*pRegisterServiceCtrlHandlerExA
)(LPCSTR
,LPHANDLER_FUNCTION_EX
,LPVOID
);
27 static HANDLE pipe_handle
= INVALID_HANDLE_VALUE
;
28 static char service_name
[100], named_pipe_name
[100];
29 static SERVICE_STATUS_HANDLE service_handle
;
31 /* Service process global variables */
32 static HANDLE service_stop_event
;
34 static void send_msg(const char *type
, const char *msg
)
39 sprintf(buf
, "%s:%s", type
, msg
);
40 WriteFile(pipe_handle
, buf
, strlen(buf
)+1, &written
, NULL
);
43 static inline void service_trace(const char *msg
)
45 send_msg("TRACE", msg
);
48 static inline void service_event(const char *event
)
50 send_msg("EVENT", event
);
53 static void service_ok(int cnd
, const char *msg
, ...)
58 va_start(valist
, msg
);
59 vsprintf(buf
, msg
, valist
);
62 send_msg(cnd
? "OK" : "FAIL", buf
);
65 static DWORD WINAPI
service_handler(DWORD ctrl
, DWORD event_type
, void *event_data
, void *context
)
67 SERVICE_STATUS status
;
69 status
.dwServiceType
= SERVICE_WIN32
;
70 status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
;
71 status
.dwWin32ExitCode
= 0;
72 status
.dwServiceSpecificExitCode
= 0;
73 status
.dwCheckPoint
= 0;
74 status
.dwWaitHint
= 0;
78 case SERVICE_CONTROL_STOP
:
79 case SERVICE_CONTROL_SHUTDOWN
:
80 service_event("STOP");
81 status
.dwCurrentState
= SERVICE_STOP_PENDING
;
82 status
.dwControlsAccepted
= 0;
83 SetServiceStatus(service_handle
, &status
);
84 SetEvent(service_stop_event
);
87 service_event("CUSTOM");
90 status
.dwCurrentState
= SERVICE_RUNNING
;
91 SetServiceStatus( service_handle
, &status
);
96 static void WINAPI
service_main(DWORD argc
, char **argv
)
98 SERVICE_STATUS status
;
101 service_ok(argc
== 1, "argc = %d\n", argc
);
103 service_ok(!strcmp(argv
[0], service_name
), "argv[0] = %s, expected %s\n", argv
[0], service_name
);
105 service_handle
= pRegisterServiceCtrlHandlerExA(service_name
, service_handler
, NULL
);
106 service_ok(service_handle
!= NULL
, "RegisterServiceCtrlHandlerEx failed: %u\n", GetLastError());
110 status
.dwServiceType
= SERVICE_WIN32
;
111 status
.dwCurrentState
= SERVICE_RUNNING
;
112 status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
113 status
.dwWin32ExitCode
= 0;
114 status
.dwServiceSpecificExitCode
= 0;
115 status
.dwCheckPoint
= 0;
116 status
.dwWaitHint
= 10000;
117 res
= SetServiceStatus(service_handle
, &status
);
118 service_ok(res
, "SetServiceStatus(SERVICE_RUNNING) failed: %u\n", GetLastError());
120 service_event("RUNNING");
122 WaitForSingleObject(service_stop_event
, INFINITE
);
124 status
.dwCurrentState
= SERVICE_STOPPED
;
125 status
.dwControlsAccepted
= 0;
126 res
= SetServiceStatus(service_handle
, &status
);
127 service_ok(res
, "SetServiceStatus(SERVICE_STOPPED) failed: %u\n", GetLastError());
130 static void service_process(void (WINAPI
*p_service_main
)(DWORD
, char **))
134 SERVICE_TABLE_ENTRYA servtbl
[] = {
135 {service_name
, p_service_main
},
139 res
= WaitNamedPipeA(named_pipe_name
, NMPWAIT_USE_DEFAULT_WAIT
);
143 pipe_handle
= CreateFileA(named_pipe_name
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
144 if(pipe_handle
== INVALID_HANDLE_VALUE
)
147 service_trace("Starting...\n");
149 service_stop_event
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
150 service_ok(service_stop_event
!= NULL
, "Could not create event: %u\n", GetLastError());
151 if(!service_stop_event
)
154 res
= StartServiceCtrlDispatcherA(servtbl
);
155 service_ok(res
, "StartServiceCtrlDispatcher failed: %u\n", GetLastError());
157 /* Let service thread terminate */
160 CloseHandle(service_stop_event
);
161 CloseHandle(pipe_handle
);
164 static DWORD WINAPI
no_stop_handler(DWORD ctrl
, DWORD event_type
, void *event_data
, void *context
)
166 SERVICE_STATUS status
;
168 status
.dwServiceType
= SERVICE_WIN32
;
169 status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
;
170 status
.dwWin32ExitCode
= 0;
171 status
.dwServiceSpecificExitCode
= 0;
172 status
.dwCheckPoint
= 0;
173 status
.dwWaitHint
= 0;
177 case SERVICE_CONTROL_STOP
:
178 case SERVICE_CONTROL_SHUTDOWN
:
179 service_event("STOP");
180 status
.dwCurrentState
= SERVICE_STOPPED
;
181 status
.dwControlsAccepted
= 0;
182 SetServiceStatus(service_handle
, &status
);
183 SetEvent(service_stop_event
);
186 status
.dwCurrentState
= SERVICE_RUNNING
;
187 SetServiceStatus( service_handle
, &status
);
192 static void WINAPI
no_stop_main(DWORD argc
, char **argv
)
194 SERVICE_STATUS status
;
197 service_ok(argc
== 1, "argc = %d\n", argc
);
199 service_ok(!strcmp(argv
[0], service_name
), "argv[0] = %s, expected %s\n", argv
[0], service_name
);
201 service_handle
= pRegisterServiceCtrlHandlerExA(service_name
, no_stop_handler
, NULL
);
202 service_ok(service_handle
!= NULL
, "RegisterServiceCtrlHandlerEx failed: %u\n", GetLastError());
206 status
.dwServiceType
= SERVICE_WIN32
;
207 status
.dwCurrentState
= SERVICE_RUNNING
;
208 status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
209 status
.dwWin32ExitCode
= 0;
210 status
.dwServiceSpecificExitCode
= 0;
211 status
.dwCheckPoint
= 0;
212 status
.dwWaitHint
= 10000;
213 res
= SetServiceStatus(service_handle
, &status
);
214 service_ok(res
, "SetServiceStatus(SERVICE_RUNNING) failed: %u\n", GetLastError());
216 service_event("RUNNING");
219 /* Test process global variables */
220 static SC_HANDLE scm_handle
;
222 static char current_event
[32];
223 static HANDLE event_handle
= INVALID_HANDLE_VALUE
;
224 static CRITICAL_SECTION event_cs
;
226 static SC_HANDLE
register_service(const char *test_name
)
228 char service_cmd
[MAX_PATH
+150], *ptr
;
231 ptr
= service_cmd
+ GetModuleFileNameA(NULL
, service_cmd
, MAX_PATH
);
233 /* If the file doesn't exist, assume we're using built-in exe and append .so to the path */
234 if(GetFileAttributesA(service_cmd
) == INVALID_FILE_ATTRIBUTES
) {
239 strcpy(ptr
, " service ");
241 sprintf(ptr
, "%s ", test_name
);
243 strcpy(ptr
, service_name
);
245 trace("service_cmd \"%s\"\n", service_cmd
);
247 service
= CreateServiceA(scm_handle
, service_name
, service_name
, GENERIC_ALL
,
248 SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
, SERVICE_ERROR_IGNORE
,
249 service_cmd
, NULL
, NULL
, NULL
, NULL
, NULL
);
250 if(!service
&& GetLastError() == ERROR_ACCESS_DENIED
) {
251 skip("Not enough access right to create service\n");
255 ok(service
!= NULL
, "CreateService failed: %u\n", GetLastError());
259 static void expect_event(const char *event_name
)
264 trace("waiting for %s\n", event_name
);
266 res
= WaitForSingleObject(event_handle
, 30000);
267 ok(res
== WAIT_OBJECT_0
, "WaitForSingleObject failed: %u\n", res
);
268 if(res
!= WAIT_OBJECT_0
)
271 EnterCriticalSection(&event_cs
);
272 strcpy(evt
, current_event
);
274 LeaveCriticalSection(&event_cs
);
276 ok(!strcmp(evt
, event_name
), "Unexpected event: %s, expected %s\n", evt
, event_name
);
279 static DWORD WINAPI
pipe_thread(void *arg
)
285 res
= ConnectNamedPipe(pipe_handle
, NULL
);
286 ok(res
|| GetLastError() == ERROR_PIPE_CONNECTED
, "ConnectNamedPipe failed: %u\n", GetLastError());
289 res
= ReadFile(pipe_handle
, buf
, sizeof(buf
), &read
, NULL
);
291 ok(GetLastError() == ERROR_BROKEN_PIPE
|| GetLastError() == ERROR_INVALID_HANDLE
,
292 "ReadFile failed: %u\n", GetLastError());
296 for(ptr
= buf
; ptr
< buf
+read
; ptr
+= strlen(ptr
)+1) {
297 if(!strncmp(ptr
, "TRACE:", 6)) {
298 trace("service trace: %s", ptr
+6);
299 }else if(!strncmp(ptr
, "OK:", 3)) {
300 ok(1, "service: %s", ptr
+3);
301 }else if(!strncmp(ptr
, "FAIL:", 5)) {
302 ok(0, "service: %s", ptr
+5);
303 }else if(!strncmp(ptr
, "EVENT:", 6)) {
304 trace("service event: %s\n", ptr
+6);
305 EnterCriticalSection(&event_cs
);
306 ok(!current_event
[0], "event %s still queued\n", current_event
);
307 strcpy(current_event
, ptr
+6);
308 LeaveCriticalSection(&event_cs
);
309 SetEvent(event_handle
);
311 ok(0, "malformed service message: %s\n", ptr
);
316 DisconnectNamedPipe(pipe_handle
);
317 trace("pipe disconnected\n");
321 static void test_service(void)
323 SC_HANDLE service_handle
= register_service("simple_service");
324 SERVICE_STATUS_PROCESS status2
;
325 SERVICE_STATUS status
;
332 trace("starting...\n");
333 res
= StartServiceA(service_handle
, 0, NULL
);
334 ok(res
, "StartService failed: %u\n", GetLastError());
336 DeleteService(service_handle
);
337 CloseServiceHandle(service_handle
);
340 expect_event("RUNNING");
342 res
= QueryServiceStatus(service_handle
, &status
);
343 ok(res
, "QueryServiceStatus failed: %d\n", GetLastError());
344 todo_wine
ok(status
.dwServiceType
== SERVICE_WIN32_OWN_PROCESS
, "status.dwServiceType = %x\n", status
.dwServiceType
);
345 ok(status
.dwCurrentState
== SERVICE_RUNNING
, "status.dwCurrentState = %x\n", status
.dwCurrentState
);
346 ok(status
.dwControlsAccepted
== (SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
),
347 "status.dwControlsAccepted = %x\n", status
.dwControlsAccepted
);
348 ok(status
.dwWin32ExitCode
== 0, "status.dwExitCode = %d\n", status
.dwWin32ExitCode
);
349 ok(status
.dwServiceSpecificExitCode
== 0, "status.dwServiceSpecificExitCode = %d\n",
350 status
.dwServiceSpecificExitCode
);
351 ok(status
.dwCheckPoint
== 0, "status.dwCheckPoint = %d\n", status
.dwCheckPoint
);
352 todo_wine
ok(status
.dwWaitHint
== 0, "status.dwWaitHint = %d\n", status
.dwWaitHint
);
354 res
= QueryServiceStatusEx(service_handle
, SC_STATUS_PROCESS_INFO
, (BYTE
*)&status2
, sizeof(status2
), &bytes
);
355 ok(res
, "QueryServiceStatusEx failed: %u\n", GetLastError());
356 ok(status2
.dwCurrentState
== SERVICE_RUNNING
, "status2.dwCurrentState = %x\n", status2
.dwCurrentState
);
357 ok(status2
.dwProcessId
!= 0, "status2.dwProcessId = %d\n", status2
.dwProcessId
);
359 res
= ControlService(service_handle
, 128, &status
);
360 ok(res
, "ControlService failed: %u\n", GetLastError());
361 expect_event("CUSTOM");
363 res
= ControlService(service_handle
, SERVICE_CONTROL_STOP
, &status
);
364 ok(res
, "ControlService failed: %u\n", GetLastError());
365 expect_event("STOP");
367 res
= DeleteService(service_handle
);
368 ok(res
, "DeleteService failed: %u\n", GetLastError());
370 CloseServiceHandle(service_handle
);
373 static inline void test_no_stop(void)
375 SC_HANDLE service_handle
= register_service("no_stop");
376 SERVICE_STATUS_PROCESS status2
;
377 SERVICE_STATUS status
;
384 trace("starting...\n");
385 res
= StartServiceA(service_handle
, 0, NULL
);
386 ok(res
, "StartService failed: %u\n", GetLastError());
388 DeleteService(service_handle
);
389 CloseServiceHandle(service_handle
);
392 expect_event("RUNNING");
394 /* Let service thread terminate */
397 res
= QueryServiceStatus(service_handle
, &status
);
398 ok(res
, "QueryServiceStatus failed: %d\n", GetLastError());
399 todo_wine
ok(status
.dwServiceType
== SERVICE_WIN32_OWN_PROCESS
, "status.dwServiceType = %x\n", status
.dwServiceType
);
400 ok(status
.dwCurrentState
== SERVICE_RUNNING
, "status.dwCurrentState = %x\n", status
.dwCurrentState
);
401 ok(status
.dwControlsAccepted
== (SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
),
402 "status.dwControlsAccepted = %x\n", status
.dwControlsAccepted
);
403 ok(status
.dwWin32ExitCode
== 0, "status.dwExitCode = %d\n", status
.dwWin32ExitCode
);
404 ok(status
.dwServiceSpecificExitCode
== 0, "status.dwServiceSpecificExitCode = %d\n",
405 status
.dwServiceSpecificExitCode
);
406 ok(status
.dwCheckPoint
== 0, "status.dwCheckPoint = %d\n", status
.dwCheckPoint
);
407 todo_wine
ok(status
.dwWaitHint
== 0, "status.dwWaitHint = %d\n", status
.dwWaitHint
);
409 res
= QueryServiceStatusEx(service_handle
, SC_STATUS_PROCESS_INFO
, (BYTE
*)&status2
, sizeof(status2
), &bytes
);
410 ok(res
, "QueryServiceStatusEx failed: %u\n", GetLastError());
411 ok(status2
.dwCurrentState
== SERVICE_RUNNING
, "status2.dwCurrentState = %x\n", status2
.dwCurrentState
);
412 ok(status2
.dwProcessId
!= 0, "status2.dwProcessId = %d\n", status2
.dwProcessId
);
414 res
= ControlService(service_handle
, SERVICE_CONTROL_STOP
, &status
);
415 ok(res
, "ControlService failed: %u\n", GetLastError());
416 expect_event("STOP");
418 res
= QueryServiceStatus(service_handle
, &status
);
419 ok(res
, "QueryServiceStatus failed: %d\n", GetLastError());
420 todo_wine
ok(status
.dwServiceType
== SERVICE_WIN32_OWN_PROCESS
, "status.dwServiceType = %x\n", status
.dwServiceType
);
421 ok(status
.dwCurrentState
==SERVICE_STOPPED
|| status
.dwCurrentState
==SERVICE_STOP_PENDING
,
422 "status.dwCurrentState = %x\n", status
.dwCurrentState
);
423 ok(status
.dwControlsAccepted
== 0, "status.dwControlsAccepted = %x\n", status
.dwControlsAccepted
);
424 ok(status
.dwWin32ExitCode
== 0, "status.dwExitCode = %d\n", status
.dwWin32ExitCode
);
425 ok(status
.dwServiceSpecificExitCode
== 0, "status.dwServiceSpecificExitCode = %d\n",
426 status
.dwServiceSpecificExitCode
);
427 ok(status
.dwCheckPoint
== 0, "status.dwCheckPoint = %d\n", status
.dwCheckPoint
);
428 ok(status
.dwWaitHint
== 0, "status.dwWaitHint = %d\n", status
.dwWaitHint
);
430 res
= QueryServiceStatusEx(service_handle
, SC_STATUS_PROCESS_INFO
, (BYTE
*)&status2
, sizeof(status2
), &bytes
);
431 ok(res
, "QueryServiceStatusEx failed: %u\n", GetLastError());
432 ok(status2
.dwProcessId
== 0 || broken(status2
.dwProcessId
!= 0),
433 "status2.dwProcessId = %d\n", status2
.dwProcessId
);
435 res
= DeleteService(service_handle
);
436 ok(res
, "DeleteService failed: %u\n", GetLastError());
438 res
= QueryServiceStatus(service_handle
, &status
);
439 ok(res
, "QueryServiceStatus failed: %d\n", GetLastError());
440 todo_wine
ok(status
.dwServiceType
== SERVICE_WIN32_OWN_PROCESS
, "status.dwServiceType = %x\n", status
.dwServiceType
);
441 ok(status
.dwCurrentState
==SERVICE_STOPPED
|| status
.dwCurrentState
==SERVICE_STOP_PENDING
,
442 "status.dwCurrentState = %x\n", status
.dwCurrentState
);
443 ok(status
.dwControlsAccepted
== 0, "status.dwControlsAccepted = %x\n", status
.dwControlsAccepted
);
444 ok(status
.dwWin32ExitCode
== 0, "status.dwExitCode = %d\n", status
.dwWin32ExitCode
);
445 ok(status
.dwServiceSpecificExitCode
== 0, "status.dwServiceSpecificExitCode = %d\n",
446 status
.dwServiceSpecificExitCode
);
447 ok(status
.dwCheckPoint
== 0, "status.dwCheckPoint = %d\n", status
.dwCheckPoint
);
448 ok(status
.dwWaitHint
== 0, "status.dwWaitHint = %d\n", status
.dwWaitHint
);
450 res
= QueryServiceStatusEx(service_handle
, SC_STATUS_PROCESS_INFO
, (BYTE
*)&status2
, sizeof(status2
), &bytes
);
451 ok(res
, "QueryServiceStatusEx failed: %u\n", GetLastError());
452 ok(status2
.dwProcessId
== 0 || broken(status2
.dwProcessId
!= 0),
453 "status2.dwProcessId = %d\n", status2
.dwProcessId
);
455 CloseServiceHandle(service_handle
);
457 res
= QueryServiceStatus(service_handle
, &status
);
458 ok(!res
, "QueryServiceStatus should have failed\n");
459 ok(GetLastError() == ERROR_INVALID_HANDLE
, "GetLastError = %d\n", GetLastError());
462 static void test_runner(void (*p_run_test
)(void))
466 sprintf(service_name
, "WineTestService%d", GetTickCount());
467 trace("service_name: %s\n", service_name
);
468 sprintf(named_pipe_name
, "\\\\.\\pipe\\%s_pipe", service_name
);
470 pipe_handle
= CreateNamedPipeA(named_pipe_name
, PIPE_ACCESS_INBOUND
,
471 PIPE_TYPE_BYTE
|PIPE_READMODE_BYTE
|PIPE_WAIT
, 10, 2048, 2048, 10000, NULL
);
472 ok(pipe_handle
!= INVALID_HANDLE_VALUE
, "CreateNamedPipe failed: %u\n", GetLastError());
473 if(pipe_handle
== INVALID_HANDLE_VALUE
)
476 event_handle
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
477 ok(event_handle
!= INVALID_HANDLE_VALUE
, "CreateEvent failed: %u\n", GetLastError());
478 if(event_handle
== INVALID_HANDLE_VALUE
)
481 thread
= CreateThread(NULL
, 0, pipe_thread
, NULL
, 0, NULL
);
482 ok(thread
!= NULL
, "CreateThread failed: %u\n", GetLastError());
488 WaitForSingleObject(thread
, INFINITE
);
489 CloseHandle(event_handle
);
490 CloseHandle(pipe_handle
);
498 InitializeCriticalSection(&event_cs
);
500 pRegisterServiceCtrlHandlerExA
= (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA");
501 if(!pRegisterServiceCtrlHandlerExA
) {
502 win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n");
506 scm_handle
= OpenSCManagerA(NULL
, NULL
, GENERIC_ALL
);
507 ok(scm_handle
!= NULL
|| GetLastError() == ERROR_ACCESS_DENIED
, "OpenSCManager failed: %u\n", GetLastError());
509 skip("OpenSCManager failed, skipping tests\n");
513 argc
= winetest_get_mainargs(&argv
);
516 test_runner(test_service
);
517 test_runner(test_no_stop
);
519 strcpy(service_name
, argv
[3]);
520 sprintf(named_pipe_name
, "\\\\.\\pipe\\%s_pipe", service_name
);
522 if(!strcmp(argv
[2], "simple_service"))
523 service_process(service_main
);
524 else if(!strcmp(argv
[2], "no_stop"))
525 service_process(no_stop_main
);
528 CloseServiceHandle(scm_handle
);