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