2 * Copyright 2022 Torge Matthies 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 #define lok ok_(__FILE__,line)
26 #define TEST_SERVICE_NAME "wine_test_svc"
27 #define TEST_SERVICE_NAME2 "wine_test_svc_2"
28 #define TEST_SERVICE_BINARY "c:\\windows\\system32\\cmd.exe"
29 #define TEST_SERVICE_BINARY_START_BOOT "\\SystemRoot\\system32\\cmd.exe"
30 #define TEST_SERVICE_BINARY_START_SYSTEM "\\??\\" TEST_SERVICE_BINARY
32 #define SC_EXIT_SUCCESS ERROR_SUCCESS
33 #define SC_EXIT_INVALID_PARAMETER ERROR_INVALID_PARAMETER
34 #define SC_EXIT_CIRCULAR_DEPENDENCY ERROR_CIRCULAR_DEPENDENCY
35 #define SC_EXIT_SERVICE_DOES_NOT_EXIST ERROR_SERVICE_DOES_NOT_EXIST
36 #define SC_EXIT_SERVICE_EXISTS ERROR_SERVICE_EXISTS
37 #define SC_EXIT_INVALID_COMMAND_LINE ERROR_INVALID_COMMAND_LINE
39 static HANDLE nul_file
;
40 static SC_HANDLE scmgr
;
42 /* Copied and modified from the reg.exe tests */
43 #define run_sc_exe(c,r) run_sc_exe_(__FILE__,__LINE__,c,r)
44 static BOOL
run_sc_exe_(const char *file
, unsigned line
, const char *cmd
, DWORD
*rc
)
46 STARTUPINFOA si
= {sizeof(STARTUPINFOA
)};
47 PROCESS_INFORMATION pi
;
52 si
.dwFlags
= STARTF_USESTDHANDLES
;
53 si
.hStdInput
= nul_file
;
54 si
.hStdOutput
= nul_file
;
55 si
.hStdError
= nul_file
;
58 if (!CreateProcessA(NULL
, cmdline
, NULL
, NULL
, TRUE
, 0, NULL
, NULL
, &si
, &pi
))
61 ret
= WaitForSingleObject(pi
.hProcess
, 10000);
62 if (ret
== WAIT_TIMEOUT
)
63 TerminateProcess(pi
.hProcess
, 1);
65 bret
= GetExitCodeProcess(pi
.hProcess
, rc
);
66 lok(bret
, "GetExitCodeProcess failed: %ld\n", GetLastError());
68 CloseHandle(pi
.hThread
);
69 CloseHandle(pi
.hProcess
);
73 #define BROKEN_CREATE 0x000000001UL
74 #define BROKEN_BINPATH 0x000000002UL
75 #define BROKEN_TYPE 0x000000004UL
76 #define BROKEN_START 0x000000008UL
77 #define BROKEN_ERROR 0x000000010UL
78 #define BROKEN_DEPEND 0x000000020UL
79 #define BROKEN_DISPLAY_NAME 0x000000040UL
80 #define BROKEN_DELAYED_AUTO_START 0x000000080UL
81 #define BROKEN_ALL ~0UL
83 #define SERVICE_DELAYED_AUTO_START (SERVICE_AUTO_START | 0x80000000)
85 #define check_service_definition(n,bi,t,s,e,de,di,br) check_service_definition_(__FILE__,__LINE__,n,bi,t,s,e,de,di,br)
86 static void check_service_definition_(const char *file
, unsigned line
, char const *name
,
87 const char *binpath
, DWORD type
, DWORD start
, DWORD error
,
88 const char *depend
, const char *display_name
, DWORD broken
)
90 SERVICE_DELAYED_AUTO_START_INFO delayed_auto_info
= {0};
93 QUERY_SERVICE_CONFIGA config
;
100 delayed_auto
= !!(start
& 0x80000000);
101 start
&= ~0x80000000;
106 svc
= OpenServiceA(scmgr
, name
, GENERIC_READ
);
107 todo_wine_if(broken
& BROKEN_CREATE
)
108 lok(!!svc
, "OpenServiceA failed: %ld\n", GetLastError());
112 ret
= QueryServiceConfigA(svc
, &cfg
.config
, sizeof(cfg
.buffer
), &needed
);
113 lok(!!ret
, "QueryServiceConfigA failed: %ld\n", GetLastError());
117 ret
= QueryServiceConfig2A(svc
, SERVICE_CONFIG_DELAYED_AUTO_START_INFO
, (LPBYTE
)&delayed_auto_info
,
118 sizeof(delayed_auto_info
), &needed
);
119 todo_wine
lok(!!ret
, "QueryServiceConfig2A(SERVICE_CONFIG_DELAYED_AUTO_START_INFO) failed: %ld\n",
122 #define check_str(a, b, msg) lok((a) && (b) && (a) != (b) && !strcmp((a), (b)), msg ": %s != %s\n", \
123 debugstr_a((a)), debugstr_a((b)))
124 #define check_dw(a, b, msg) lok((a) == (b), msg ": 0x%lx != 0x%lx\n", a, b)
126 todo_wine_if(broken
& BROKEN_BINPATH
)
127 check_str(cfg
.config
.lpBinaryPathName
, binpath
, "Wrong binary path");
128 todo_wine_if(broken
& BROKEN_TYPE
)
129 check_dw(cfg
.config
.dwServiceType
, type
, "Wrong service type");
130 todo_wine_if(broken
& BROKEN_START
)
131 check_dw(cfg
.config
.dwStartType
, start
, "Wrong start type");
132 todo_wine_if(broken
& BROKEN_ERROR
)
133 check_dw(cfg
.config
.dwErrorControl
, error
, "Wrong error control");
134 todo_wine_if(broken
& BROKEN_DEPEND
)
135 check_str(cfg
.config
.lpDependencies
, depend
, "Wrong dependencies");
136 todo_wine_if(broken
& BROKEN_DISPLAY_NAME
)
137 check_str(cfg
.config
.lpDisplayName
, display_name
, "Wrong display name");
138 todo_wine_if(broken
& BROKEN_DELAYED_AUTO_START
)
139 check_dw((DWORD
)delayed_auto_info
.fDelayedAutostart
, (DWORD
)delayed_auto
, "Wrong delayed autostart value");
145 CloseServiceHandle(svc
);
148 #define delete_service(n,e,b) delete_service_(__FILE__,__LINE__,n,e,b)
149 static void delete_service_(const char *file
, unsigned line
, const char *name
, DWORD expected_status
, BOOL broken
)
155 strcpy(command
, "sc delete ");
156 strcat(command
, name
);
157 bret
= run_sc_exe_(file
, line
, command
, &r
);
158 lok(bret
, "run_sc_exe failed\n");
159 if (expected_status
!= SC_EXIT_SUCCESS
&& !strcmp(winetest_platform
, "wine"))
161 todo_wine_if(broken
) lok(r
== expected_status
, "got exit code %ld, expected %ld\n", r
, expected_status
);
164 static void test_create_service(BOOL elevated
)
168 DWORD expected_start_type
;
169 DWORD expected_service_type
;
170 const char * expected_binary_path
;
173 { "boot type= kernel", SERVICE_BOOT_START
, SERVICE_KERNEL_DRIVER
, TEST_SERVICE_BINARY_START_BOOT
,
174 BROKEN_BINPATH
| BROKEN_DISPLAY_NAME
},
175 { "system type= kernel", SERVICE_SYSTEM_START
, SERVICE_KERNEL_DRIVER
, TEST_SERVICE_BINARY_START_SYSTEM
,
176 BROKEN_BINPATH
| BROKEN_DISPLAY_NAME
},
177 { "auto", SERVICE_AUTO_START
, SERVICE_WIN32_OWN_PROCESS
, TEST_SERVICE_BINARY
,
178 BROKEN_DISPLAY_NAME
},
179 { "demand", SERVICE_DEMAND_START
, SERVICE_WIN32_OWN_PROCESS
, TEST_SERVICE_BINARY
,
180 BROKEN_DISPLAY_NAME
},
181 { "disabled", SERVICE_DISABLED
, SERVICE_WIN32_OWN_PROCESS
, TEST_SERVICE_BINARY
,
182 BROKEN_DISPLAY_NAME
},
183 { "delayed-auto", SERVICE_DELAYED_AUTO_START
, SERVICE_WIN32_OWN_PROCESS
, TEST_SERVICE_BINARY
,
184 BROKEN_START
| BROKEN_DISPLAY_NAME
| BROKEN_DELAYED_AUTO_START
}
188 DWORD expected_error_control
;
189 } error_severities
[] = {
190 { "normal", SERVICE_ERROR_NORMAL
},
191 { "severe", SERVICE_ERROR_SEVERE
},
192 { "critical", SERVICE_ERROR_CRITICAL
},
193 { "ignore", SERVICE_ERROR_IGNORE
}
200 win_skip("\"sc create\" tests need elevated permissions\n");
204 #define check_exit_code(x) ok(r == (x), "got exit code %ld, expected %d\n", r, (x))
205 #define check_test_service(t,s,e,de,di,br) \
206 check_service_definition(TEST_SERVICE_NAME, TEST_SERVICE_BINARY, t, s, e, de, di ? di : TEST_SERVICE_NAME, br)
207 #define delete_test_service(x, y) \
208 delete_service(TEST_SERVICE_NAME, (x) ? SC_EXIT_SUCCESS : SC_EXIT_SERVICE_DOES_NOT_EXIST, (y))
209 #define check_test_service2(t,s,e,de,di,br) \
210 check_service_definition(TEST_SERVICE_NAME2, TEST_SERVICE_BINARY, t, s, e, de, di ? di : TEST_SERVICE_NAME2, br)
211 #define delete_test_service2(x, y) \
212 delete_service(TEST_SERVICE_NAME2, (x) ? SC_EXIT_SUCCESS : SC_EXIT_SERVICE_DOES_NOT_EXIST, (y))
214 /* too few parameters */
216 run_sc_exe("sc create", &r
);
217 todo_wine
check_exit_code(SC_EXIT_INVALID_COMMAND_LINE
);
218 delete_test_service(FALSE
, FALSE
);
220 run_sc_exe("sc create " TEST_SERVICE_NAME
, &r
);
221 todo_wine
check_exit_code(SC_EXIT_INVALID_COMMAND_LINE
);
222 delete_test_service(FALSE
, FALSE
);
226 run_sc_exe("sc create " TEST_SERVICE_NAME
" binpath= \"" TEST_SERVICE_BINARY
"\"", &r
);
227 check_exit_code(SC_EXIT_SUCCESS
);
228 check_test_service(SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
, SERVICE_ERROR_NORMAL
, "", NULL
,
229 BROKEN_DISPLAY_NAME
);
231 /* existing service */
233 run_sc_exe("sc create " TEST_SERVICE_NAME
" binpath= \"" TEST_SERVICE_BINARY
"\" start= auto", &r
);
234 todo_wine
check_exit_code(SC_EXIT_SERVICE_EXISTS
);
235 check_test_service(SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
, SERVICE_ERROR_NORMAL
, "", NULL
,
236 BROKEN_DISPLAY_NAME
);
237 delete_test_service(TRUE
, FALSE
);
241 run_sc_exe("sc create " TEST_SERVICE_NAME
" binpath= \"" TEST_SERVICE_BINARY
"\" type= invalid", &r
);
242 todo_wine
check_exit_code(SC_EXIT_INVALID_COMMAND_LINE
);
243 delete_test_service(FALSE
, TRUE
);
245 run_sc_exe("sc create " TEST_SERVICE_NAME
" binpath= \"" TEST_SERVICE_BINARY
"\" type= own", &r
);
246 check_exit_code(SC_EXIT_SUCCESS
);
247 check_test_service(SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
, SERVICE_ERROR_NORMAL
, "", NULL
,
248 BROKEN_DISPLAY_NAME
);
249 delete_test_service(TRUE
, FALSE
);
251 run_sc_exe("sc create " TEST_SERVICE_NAME
" binpath= \"" TEST_SERVICE_BINARY
"\" type= interact", &r
);
252 todo_wine
check_exit_code(SC_EXIT_INVALID_PARAMETER
);
253 delete_test_service(FALSE
, TRUE
);
255 run_sc_exe("sc create " TEST_SERVICE_NAME
" binpath= \"" TEST_SERVICE_BINARY
"\" type= interact type= own", &r
);
256 check_exit_code(SC_EXIT_SUCCESS
);
257 check_test_service(SERVICE_WIN32_OWN_PROCESS
| SERVICE_INTERACTIVE_PROCESS
, SERVICE_DEMAND_START
,
258 SERVICE_ERROR_NORMAL
, "", NULL
, BROKEN_TYPE
| BROKEN_DISPLAY_NAME
);
259 delete_test_service(TRUE
, FALSE
);
263 run_sc_exe("sc create " TEST_SERVICE_NAME
" binpath= \"" TEST_SERVICE_BINARY
"\" start= invalid", &r
);
264 todo_wine
check_exit_code(SC_EXIT_INVALID_COMMAND_LINE
);
265 delete_test_service(FALSE
, TRUE
);
267 for (i
= 0; i
< ARRAY_SIZE(start_types
); i
++)
271 strcpy(cmdline
, "sc create " TEST_SERVICE_NAME
" binpath= \"" TEST_SERVICE_BINARY
"\" start= ");
272 strcat(cmdline
, start_types
[i
].param
);
273 run_sc_exe(cmdline
, &r
);
274 check_exit_code(SC_EXIT_SUCCESS
);
275 check_service_definition(TEST_SERVICE_NAME
, start_types
[i
].expected_binary_path
,
276 start_types
[i
].expected_service_type
, start_types
[i
].expected_start_type
,
277 SERVICE_ERROR_NORMAL
, "", TEST_SERVICE_NAME
, start_types
[i
].broken
);
278 delete_test_service(TRUE
, FALSE
);
283 for (i
= 0; i
< ARRAY_SIZE(error_severities
); i
++)
287 strcpy(cmdline
, "sc create " TEST_SERVICE_NAME
" binpath= \"" TEST_SERVICE_BINARY
"\" error= ");
288 strcat(cmdline
, error_severities
[i
].param
);
289 run_sc_exe(cmdline
, &r
);
290 check_exit_code(SC_EXIT_SUCCESS
);
291 check_test_service(SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
,
292 error_severities
[i
].expected_error_control
, "", NULL
, BROKEN_DISPLAY_NAME
);
293 delete_test_service(TRUE
, FALSE
);
298 run_sc_exe("sc create " TEST_SERVICE_NAME
" binpath= \"" TEST_SERVICE_BINARY
"\" tag= yes", &r
);
299 todo_wine
check_exit_code(SC_EXIT_INVALID_PARAMETER
);
300 delete_test_service(FALSE
, TRUE
);
302 run_sc_exe("sc create " TEST_SERVICE_NAME
" binpath= \"" TEST_SERVICE_BINARY
"\" tag= no", &r
);
303 check_exit_code(SC_EXIT_SUCCESS
);
304 check_test_service(SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
, SERVICE_ERROR_NORMAL
, "", NULL
,
305 BROKEN_DISPLAY_NAME
);
306 delete_test_service(TRUE
, FALSE
);
310 run_sc_exe("sc create " TEST_SERVICE_NAME
" binpath= \"" TEST_SERVICE_BINARY
"\" depend= " TEST_SERVICE_NAME
, &r
);
311 todo_wine
check_exit_code(SC_EXIT_CIRCULAR_DEPENDENCY
);
312 delete_test_service(FALSE
, TRUE
);
314 run_sc_exe("sc create " TEST_SERVICE_NAME2
" binpath= \"" TEST_SERVICE_BINARY
"\" depend= " TEST_SERVICE_NAME
, &r
);
315 check_exit_code(SC_EXIT_SUCCESS
);
316 check_test_service2(SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
, SERVICE_ERROR_NORMAL
,
317 TEST_SERVICE_NAME
, NULL
, BROKEN_DEPEND
| BROKEN_DISPLAY_NAME
);
318 delete_test_service2(TRUE
, FALSE
);
320 run_sc_exe("sc create " TEST_SERVICE_NAME
" binpath= " TEST_SERVICE_BINARY
, &r
);
321 check_exit_code(SC_EXIT_SUCCESS
);
322 run_sc_exe("sc create " TEST_SERVICE_NAME2
" binpath= \"" TEST_SERVICE_BINARY
"\" depend= " TEST_SERVICE_NAME
, &r
);
323 check_exit_code(SC_EXIT_SUCCESS
);
324 check_test_service2(SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
, SERVICE_ERROR_NORMAL
,
325 TEST_SERVICE_NAME
, NULL
, BROKEN_DEPEND
| BROKEN_DISPLAY_NAME
);
326 delete_test_service2(TRUE
, FALSE
);
327 delete_test_service(TRUE
, FALSE
);
331 run_sc_exe("sc create " TEST_SERVICE_NAME
" binpath= " TEST_SERVICE_BINARY
332 " displayname= \"Wine Test Service\"", &r
);
333 check_exit_code(SC_EXIT_SUCCESS
);
334 check_test_service(SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
, SERVICE_ERROR_NORMAL
, "",
335 "Wine Test Service", 0);
336 delete_test_service(TRUE
, FALSE
);
340 run_sc_exe("sc create " TEST_SERVICE_NAME2
" binpath=\"" TEST_SERVICE_BINARY
"\" type=own start=auto"
341 " error=normal tag=no depend=" TEST_SERVICE_NAME
" displayname=\"Wine Test Service\"", &r
);
342 ok(r
== SC_EXIT_SUCCESS
|| broken(r
== SC_EXIT_INVALID_COMMAND_LINE
), "got exit code %ld, expected %d\n",
344 if (r
== SC_EXIT_SUCCESS
)
346 check_test_service2(SERVICE_WIN32_OWN_PROCESS
, SERVICE_AUTO_START
, SERVICE_ERROR_NORMAL
,
347 TEST_SERVICE_NAME
, "Wine Test Service", BROKEN_DEPEND
);
348 delete_test_service2(TRUE
, FALSE
);
352 delete_test_service2(FALSE
, FALSE
);
355 /* case-insensitive */
357 run_sc_exe("SC CREATE " TEST_SERVICE_NAME2
" BINPATH= \"" TEST_SERVICE_BINARY
"\" TYPE= OWN START= AUTO"
358 " ERROR= NORMAL TAG= NO DEPEND= " TEST_SERVICE_NAME
" DISPLAYNAME= \"Wine Test Service\"", &r
);
359 check_exit_code(SC_EXIT_SUCCESS
);
360 check_test_service2(SERVICE_WIN32_OWN_PROCESS
, SERVICE_AUTO_START
, SERVICE_ERROR_NORMAL
,
361 TEST_SERVICE_NAME
, "Wine Test Service", BROKEN_DEPEND
);
362 delete_test_service2(TRUE
, FALSE
);
364 #undef delete_test_service2
365 #undef check_test_service2
366 #undef delete_test_service
367 #undef check_test_service
368 #undef check_exit_code
371 /* taken from winetest, only whitespace changes */
372 static int running_elevated(void)
375 TOKEN_ELEVATION elevation_info
;
378 /* Get the process token */
379 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY
, &token
))
382 /* Get the elevation info from the token */
383 if (!GetTokenInformation(token
, TokenElevation
, &elevation_info
, sizeof(TOKEN_ELEVATION
), &size
))
390 return elevation_info
.TokenIsElevated
;
396 SECURITY_ATTRIBUTES secattr
= {sizeof(SECURITY_ATTRIBUTES
), NULL
, TRUE
};
397 BOOL elevated
= running_elevated();
399 nul_file
= CreateFileA("NUL", GENERIC_READ
| GENERIC_WRITE
, 0, &secattr
, OPEN_EXISTING
,
400 FILE_ATTRIBUTE_NORMAL
, NULL
);
402 scmgr
= OpenSCManagerA(NULL
, NULL
, GENERIC_READ
);
403 ok(!!scmgr
, "OpenSCManagerA failed: %ld\n", GetLastError());
405 test_create_service(elevated
);
407 CloseServiceHandle(scmgr
);
408 CloseHandle(nul_file
);