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