Release 1.5.29.
[wine] / programs / services / tests / 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     default:
86         status.dwCurrentState = SERVICE_RUNNING;
87         SetServiceStatus( service_handle, &status );
88         return NO_ERROR;
89     }
90 }
91
92 static void WINAPI service_main(DWORD argc, char **argv)
93 {
94     SERVICE_STATUS status;
95     BOOL res;
96
97     service_ok(argc == 1, "argc = %d", argc);
98     if(argc)
99         service_ok(!strcmp(argv[0], service_name), "argv[0] = %s, expected %s", argv[0], service_name);
100
101     service_handle = pRegisterServiceCtrlHandlerExA(service_name, service_handler, NULL);
102     service_ok(service_handle != NULL, "RegisterServiceCtrlHandlerEx failed: %u\n", GetLastError());
103     if(!service_handle)
104         return;
105
106     status.dwServiceType             = SERVICE_WIN32;
107     status.dwCurrentState            = SERVICE_RUNNING;
108     status.dwControlsAccepted        = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
109     status.dwWin32ExitCode           = 0;
110     status.dwServiceSpecificExitCode = 0;
111     status.dwCheckPoint              = 0;
112     status.dwWaitHint                = 10000;
113     res = SetServiceStatus(service_handle, &status);
114     service_ok(res, "SetServiceStatus(SERVICE_RUNNING) failed: %u", GetLastError());
115
116     service_event("RUNNING");
117
118     WaitForSingleObject(service_stop_event, INFINITE);
119
120     status.dwCurrentState     = SERVICE_STOPPED;
121     status.dwControlsAccepted = 0;
122     res = SetServiceStatus(service_handle, &status);
123     service_ok(res, "SetServiceStatus(SERVICE_STOPPED) failed: %u", GetLastError());
124 }
125
126 static void service_process(void (WINAPI *p_service_main)(DWORD, char **))
127 {
128     BOOL res;
129
130     SERVICE_TABLE_ENTRYA servtbl[] = {
131         {service_name, p_service_main},
132         {NULL, NULL}
133     };
134
135     res = WaitNamedPipeA(named_pipe_name, NMPWAIT_USE_DEFAULT_WAIT);
136     if(!res)
137         return;
138
139     pipe_handle = CreateFileA(named_pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
140     if(pipe_handle == INVALID_HANDLE_VALUE)
141         return;
142
143     service_trace("Starting...");
144
145     service_stop_event = CreateEventA(NULL, TRUE, FALSE, NULL);
146     service_ok(service_stop_event != NULL, "Could not create event: %u\n", GetLastError());
147     if(!service_stop_event)
148         return;
149
150     res = StartServiceCtrlDispatcherA(servtbl);
151     service_ok(res, "StartServiceCtrlDispatcher failed: %u\n", GetLastError());
152
153     /* Let service thread terminate */
154     Sleep(50);
155
156     CloseHandle(service_stop_event);
157     CloseHandle(pipe_handle);
158 }
159
160 static DWORD WINAPI no_stop_handler(DWORD ctrl, DWORD event_type, void *event_data, void *context)
161 {
162     SERVICE_STATUS status;
163
164     status.dwServiceType             = SERVICE_WIN32;
165     status.dwControlsAccepted        = SERVICE_ACCEPT_STOP;
166     status.dwWin32ExitCode           = 0;
167     status.dwServiceSpecificExitCode = 0;
168     status.dwCheckPoint              = 0;
169     status.dwWaitHint                = 0;
170
171     switch(ctrl)
172     {
173         case SERVICE_CONTROL_STOP:
174         case SERVICE_CONTROL_SHUTDOWN:
175             service_event("STOP");
176             status.dwCurrentState     = SERVICE_STOPPED;
177             status.dwControlsAccepted = 0;
178             SetServiceStatus(service_handle, &status);
179             SetEvent(service_stop_event);
180             return NO_ERROR;
181         default:
182             status.dwCurrentState = SERVICE_RUNNING;
183             SetServiceStatus( service_handle, &status );
184             return NO_ERROR;
185     }
186 }
187
188 static void WINAPI no_stop_main(DWORD argc, char **argv)
189 {
190     SERVICE_STATUS status;
191     BOOL res;
192
193     service_ok(argc == 1, "argc = %d", argc);
194     if(argc)
195         service_ok(!strcmp(argv[0], service_name), "argv[0] = %s, expected %s", argv[0], service_name);
196
197     service_handle = pRegisterServiceCtrlHandlerExA(service_name, no_stop_handler, NULL);
198     service_ok(service_handle != NULL, "RegisterServiceCtrlHandlerEx failed: %u\n", GetLastError());
199     if(!service_handle)
200         return;
201
202     status.dwServiceType             = SERVICE_WIN32;
203     status.dwCurrentState            = SERVICE_RUNNING;
204     status.dwControlsAccepted        = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
205     status.dwWin32ExitCode           = 0;
206     status.dwServiceSpecificExitCode = 0;
207     status.dwCheckPoint              = 0;
208     status.dwWaitHint                = 10000;
209     res = SetServiceStatus(service_handle, &status);
210     service_ok(res, "SetServiceStatus(SERVICE_RUNNING) failed: %u", GetLastError());
211
212     service_event("RUNNING");
213 }
214
215 /* Test process global variables */
216 static SC_HANDLE scm_handle;
217
218 static char current_event[32];
219 static HANDLE event_handle = INVALID_HANDLE_VALUE;
220 static CRITICAL_SECTION event_cs;
221
222 static SC_HANDLE register_service(const char *test_name)
223 {
224     char service_cmd[MAX_PATH+150], *ptr;
225     SC_HANDLE service;
226
227     ptr = service_cmd + GetModuleFileNameA(NULL, service_cmd, MAX_PATH);
228
229     /* If the file doesn't exist, assume we're using built-in exe and append .so to the path */
230     if(GetFileAttributesA(service_cmd) == INVALID_FILE_ATTRIBUTES) {
231         strcpy(ptr, ".so");
232         ptr += 3;
233     }
234
235     strcpy(ptr, " service ");
236     ptr += strlen(ptr);
237     sprintf(ptr, "%s ", test_name);
238     ptr += strlen(ptr);
239     strcpy(ptr, service_name);
240
241     trace("service_cmd \"%s\"\n", service_cmd);
242
243     service = CreateServiceA(scm_handle, service_name, service_name, GENERIC_ALL,
244                              SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE,
245                              service_cmd, NULL, NULL, NULL, NULL, NULL);
246     if(!service && GetLastError() == ERROR_ACCESS_DENIED) {
247         skip("Not enough access right to create service\n");
248         return NULL;
249     }
250
251     ok(service != NULL, "CreateService failed: %u\n", GetLastError());
252     return service;
253 }
254
255 static void expect_event(const char *event_name)
256 {
257     char evt[32];
258     DWORD res;
259
260     trace("waiting for %s\n", event_name);
261
262     res = WaitForSingleObject(event_handle, 30000);
263     ok(res == WAIT_OBJECT_0, "WaitForSingleObject failed: %u\n", res);
264     if(res != WAIT_OBJECT_0)
265         return;
266
267     EnterCriticalSection(&event_cs);
268     strcpy(evt, current_event);
269     *current_event = 0;
270     LeaveCriticalSection(&event_cs);
271
272     ok(!strcmp(evt, event_name), "Unexpected event: %s, expected %s\n", evt, event_name);
273 }
274
275 static DWORD WINAPI pipe_thread(void *arg)
276 {
277     char buf[257], *ptr;
278     DWORD read;
279     BOOL res;
280
281     res = ConnectNamedPipe(pipe_handle, NULL);
282     ok(res || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe failed: %u\n", GetLastError());
283
284     while(1) {
285         res = ReadFile(pipe_handle, buf, sizeof(buf), &read, NULL);
286         if(!res) {
287             ok(GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_INVALID_HANDLE,
288                "ReadFile failed: %u\n", GetLastError());
289             break;
290         }
291
292         for(ptr = buf; ptr < buf+read; ptr += strlen(ptr)+1) {
293             if(!strncmp(ptr, "TRACE:", 6)) {
294                 trace("service trace: %s\n", ptr+6);
295             }else if(!strncmp(ptr, "OK:", 3)) {
296                 ok(1, "service: %s\n", ptr+3);
297             }else if(!strncmp(ptr, "FAIL:", 5)) {
298                 ok(0, "service: %s\n", ptr+5);
299             }else if(!strncmp(ptr, "EVENT:", 6)) {
300                 trace("service event: %s\n", ptr+6);
301                 EnterCriticalSection(&event_cs);
302                 ok(!current_event[0], "event %s still queued\n", current_event);
303                 strcpy(current_event, ptr+6);
304                 LeaveCriticalSection(&event_cs);
305                 SetEvent(event_handle);
306             }else {
307                 ok(0, "malformed service message: %s\n", ptr);
308             }
309         }
310     }
311
312     DisconnectNamedPipe(pipe_handle);
313     trace("pipe disconnected\n");
314     return 0;
315 }
316
317 static void test_service(void)
318 {
319     SC_HANDLE service_handle = register_service("simple_service");
320     SERVICE_STATUS status;
321     BOOL res;
322
323     if(!service_handle)
324         return;
325
326     trace("starting...\n");
327     res = StartServiceA(service_handle, 0, NULL);
328     ok(res, "StartService failed: %u\n", GetLastError());
329     if(!res) {
330         DeleteService(service_handle);
331         return;
332     }
333     expect_event("RUNNING");
334
335     res = QueryServiceStatus(service_handle, &status);
336     ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
337     todo_wine ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
338     ok(status.dwCurrentState == SERVICE_RUNNING, "status.dwCurrentState = %x\n", status.dwCurrentState);
339     ok(status.dwControlsAccepted == (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN),
340             "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
341     ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
342     ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
343             status.dwServiceSpecificExitCode);
344     ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
345     todo_wine ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
346
347     res = ControlService(service_handle, SERVICE_CONTROL_STOP, &status);
348     ok(res, "ControlService failed: %u\n", GetLastError());
349     expect_event("STOP");
350
351     res = DeleteService(service_handle);
352     ok(res, "DeleteService failed: %u\n", GetLastError());
353
354     CloseServiceHandle(service_handle);
355 }
356
357 static inline void test_no_stop(void)
358 {
359     SC_HANDLE service_handle = register_service("no_stop");
360     SERVICE_STATUS status;
361     BOOL res;
362
363     if(!service_handle)
364         return;
365
366     trace("starting...\n");
367     res = StartServiceA(service_handle, 0, NULL);
368     ok(res, "StartService failed: %u\n", GetLastError());
369     if(!res) {
370         DeleteService(service_handle);
371         return;
372     }
373     expect_event("RUNNING");
374
375     /* Let service thread terminate */
376     Sleep(1000);
377
378     res = QueryServiceStatus(service_handle, &status);
379     ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
380     todo_wine ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
381     ok(status.dwCurrentState == SERVICE_RUNNING, "status.dwCurrentState = %x\n", status.dwCurrentState);
382     ok(status.dwControlsAccepted == (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN),
383             "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
384     ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
385     ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
386             status.dwServiceSpecificExitCode);
387     ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
388     todo_wine ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
389
390     res = ControlService(service_handle, SERVICE_CONTROL_STOP, &status);
391     ok(res, "ControlService failed: %u\n", GetLastError());
392     expect_event("STOP");
393
394     res = QueryServiceStatus(service_handle, &status);
395     ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
396     todo_wine ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
397     ok(status.dwCurrentState==SERVICE_STOPPED || status.dwCurrentState==SERVICE_STOP_PENDING,
398             "status.dwCurrentState = %x\n", status.dwCurrentState);
399     ok(status.dwControlsAccepted == 0, "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
400     ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
401     ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
402             status.dwServiceSpecificExitCode);
403     ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
404     ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
405
406     res = DeleteService(service_handle);
407     ok(res, "DeleteService failed: %u\n", GetLastError());
408
409     res = QueryServiceStatus(service_handle, &status);
410     ok(res, "QueryServiceStatus failed: %d\n", GetLastError());
411     todo_wine ok(status.dwServiceType == SERVICE_WIN32_OWN_PROCESS, "status.dwServiceType = %x\n", status.dwServiceType);
412     ok(status.dwCurrentState==SERVICE_STOPPED || status.dwCurrentState==SERVICE_STOP_PENDING,
413             "status.dwCurrentState = %x\n", status.dwCurrentState);
414     ok(status.dwControlsAccepted == 0, "status.dwControlsAccepted = %x\n", status.dwControlsAccepted);
415     ok(status.dwWin32ExitCode == 0, "status.dwExitCode = %d\n", status.dwWin32ExitCode);
416     ok(status.dwServiceSpecificExitCode == 0, "status.dwServiceSpecificExitCode = %d\n",
417             status.dwServiceSpecificExitCode);
418     ok(status.dwCheckPoint == 0, "status.dwCheckPoint = %d\n", status.dwCheckPoint);
419     ok(status.dwWaitHint == 0, "status.dwWaitHint = %d\n", status.dwWaitHint);
420
421     CloseServiceHandle(service_handle);
422
423     res = QueryServiceStatus(service_handle, &status);
424     ok(!res, "QueryServiceStatus should have failed\n");
425     ok(GetLastError() == ERROR_INVALID_HANDLE, "GetLastError = %d\n", GetLastError());
426 }
427
428 static void test_runner(void (*p_run_test)(void))
429 {
430     HANDLE thread;
431
432     sprintf(service_name, "WineTestService%d", GetTickCount());
433     trace("service_name: %s\n", service_name);
434     sprintf(named_pipe_name, "\\\\.\\pipe\\%s_pipe", service_name);
435
436     pipe_handle = CreateNamedPipeA(named_pipe_name, PIPE_ACCESS_INBOUND,
437                                    PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, 10, 2048, 2048, 10000, NULL);
438     ok(pipe_handle != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %u\n", GetLastError());
439     if(pipe_handle == INVALID_HANDLE_VALUE)
440         return;
441
442     InitializeCriticalSection(&event_cs);
443     event_handle = CreateEventA(NULL, FALSE, FALSE, NULL);
444     ok(event_handle != INVALID_HANDLE_VALUE, "CreateEvent failed: %u\n", GetLastError());
445     if(event_handle == INVALID_HANDLE_VALUE)
446         return;
447
448     thread = CreateThread(NULL, 0, pipe_thread, NULL, 0, NULL);
449     ok(thread != NULL, "CreateThread failed: %u\n", GetLastError());
450     if(!thread)
451         return;
452
453     p_run_test();
454
455     WaitForSingleObject(thread, INFINITE);
456     CloseHandle(event_handle);
457     CloseHandle(pipe_handle);
458 }
459
460 START_TEST(service)
461 {
462     char **argv;
463     int argc;
464
465     pRegisterServiceCtrlHandlerExA = (void*)GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegisterServiceCtrlHandlerExA");
466     if(!pRegisterServiceCtrlHandlerExA) {
467         win_skip("RegisterServiceCtrlHandlerExA not available, skipping tests\n");
468         return;
469     }
470
471     argc = winetest_get_mainargs(&argv);
472
473     scm_handle = OpenSCManagerA(NULL, NULL, GENERIC_ALL);
474     ok(scm_handle != NULL, "OpenSCManager failed: %u\n", GetLastError());
475
476     if(argc < 3) {
477         test_runner(test_service);
478         test_runner(test_no_stop);
479     }else {
480         strcpy(service_name, argv[3]);
481         sprintf(named_pipe_name, "\\\\.\\pipe\\%s_pipe", service_name);
482
483         if(!strcmp(argv[2], "simple_service"))
484             service_process(service_main);
485         else if(!strcmp(argv[2], "no_stop"))
486             service_process(no_stop_main);
487     }
488
489     CloseServiceHandle(scm_handle);
490 }