sc.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. /*
  2. * Copyright 2022 Torge Matthies 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. #include <windows.h>
  19. #include <winsvc.h>
  20. #include <stdio.h>
  21. #include "wine/test.h"
  22. #define lok ok_(__FILE__,line)
  23. #define TEST_SERVICE_NAME "wine_test_svc"
  24. #define TEST_SERVICE_NAME2 "wine_test_svc_2"
  25. #define TEST_SERVICE_BINARY "c:\\windows\\system32\\cmd.exe"
  26. #define TEST_SERVICE_BINARY_START_BOOT "\\SystemRoot\\system32\\cmd.exe"
  27. #define TEST_SERVICE_BINARY_START_SYSTEM "\\??\\" TEST_SERVICE_BINARY
  28. #define SC_EXIT_SUCCESS ERROR_SUCCESS
  29. #define SC_EXIT_INVALID_PARAMETER ERROR_INVALID_PARAMETER
  30. #define SC_EXIT_CIRCULAR_DEPENDENCY ERROR_CIRCULAR_DEPENDENCY
  31. #define SC_EXIT_SERVICE_DOES_NOT_EXIST ERROR_SERVICE_DOES_NOT_EXIST
  32. #define SC_EXIT_SERVICE_EXISTS ERROR_SERVICE_EXISTS
  33. #define SC_EXIT_INVALID_COMMAND_LINE ERROR_INVALID_COMMAND_LINE
  34. static HANDLE nul_file;
  35. static SC_HANDLE scmgr;
  36. /* Copied and modified from the reg.exe tests */
  37. #define run_sc_exe(c,r) run_sc_exe_(__FILE__,__LINE__,c,r)
  38. static BOOL run_sc_exe_(const char *file, unsigned line, const char *cmd, DWORD *rc)
  39. {
  40. STARTUPINFOA si = {sizeof(STARTUPINFOA)};
  41. PROCESS_INFORMATION pi;
  42. BOOL bret;
  43. DWORD ret;
  44. char cmdline[256];
  45. si.dwFlags = STARTF_USESTDHANDLES;
  46. si.hStdInput = nul_file;
  47. si.hStdOutput = nul_file;
  48. si.hStdError = nul_file;
  49. strcpy(cmdline, cmd);
  50. if (!CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
  51. return FALSE;
  52. ret = WaitForSingleObject(pi.hProcess, 10000);
  53. if (ret == WAIT_TIMEOUT)
  54. TerminateProcess(pi.hProcess, 1);
  55. bret = GetExitCodeProcess(pi.hProcess, rc);
  56. lok(bret, "GetExitCodeProcess failed: %ld\n", GetLastError());
  57. CloseHandle(pi.hThread);
  58. CloseHandle(pi.hProcess);
  59. return bret;
  60. }
  61. #define BROKEN_CREATE 0x000000001UL
  62. #define BROKEN_BINPATH 0x000000002UL
  63. #define BROKEN_TYPE 0x000000004UL
  64. #define BROKEN_START 0x000000008UL
  65. #define BROKEN_ERROR 0x000000010UL
  66. #define BROKEN_DEPEND 0x000000020UL
  67. #define BROKEN_DISPLAY_NAME 0x000000040UL
  68. #define BROKEN_DELAYED_AUTO_START 0x000000080UL
  69. #define BROKEN_ALL ~0UL
  70. #define SERVICE_DELAYED_AUTO_START (SERVICE_AUTO_START | 0x80000000)
  71. #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)
  72. static void check_service_definition_(const char *file, unsigned line, char const *name,
  73. const char *binpath, DWORD type, DWORD start, DWORD error,
  74. const char *depend, const char *display_name, DWORD broken)
  75. {
  76. SERVICE_DELAYED_AUTO_START_INFO delayed_auto_info = {0};
  77. union {
  78. char buffer[8192];
  79. QUERY_SERVICE_CONFIGA config;
  80. } cfg;
  81. BOOL delayed_auto;
  82. SC_HANDLE svc;
  83. DWORD needed;
  84. BOOL ret;
  85. delayed_auto = !!(start & 0x80000000);
  86. start &= ~0x80000000;
  87. if (!scmgr)
  88. return;
  89. svc = OpenServiceA(scmgr, name, GENERIC_READ);
  90. todo_wine_if(broken & BROKEN_CREATE)
  91. lok(!!svc, "OpenServiceA failed: %ld\n", GetLastError());
  92. if (!svc)
  93. return;
  94. ret = QueryServiceConfigA(svc, &cfg.config, sizeof(cfg.buffer), &needed);
  95. lok(!!ret, "QueryServiceConfigA failed: %ld\n", GetLastError());
  96. if (!ret)
  97. goto done;
  98. ret = QueryServiceConfig2A(svc, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (LPBYTE)&delayed_auto_info,
  99. sizeof(delayed_auto_info), &needed);
  100. todo_wine lok(!!ret, "QueryServiceConfig2A(SERVICE_CONFIG_DELAYED_AUTO_START_INFO) failed: %ld\n",
  101. GetLastError());
  102. #define check_str(a, b, msg) lok((a) && (b) && (a) != (b) && !strcmp((a), (b)), msg ": %s != %s\n", \
  103. debugstr_a((a)), debugstr_a((b)))
  104. #define check_dw(a, b, msg) lok((a) == (b), msg ": 0x%lx != 0x%lx\n", a, b)
  105. todo_wine_if(broken & BROKEN_BINPATH)
  106. check_str(cfg.config.lpBinaryPathName, binpath, "Wrong binary path");
  107. todo_wine_if(broken & BROKEN_TYPE)
  108. check_dw(cfg.config.dwServiceType, type, "Wrong service type");
  109. todo_wine_if(broken & BROKEN_START)
  110. check_dw(cfg.config.dwStartType, start, "Wrong start type");
  111. todo_wine_if(broken & BROKEN_ERROR)
  112. check_dw(cfg.config.dwErrorControl, error, "Wrong error control");
  113. todo_wine_if(broken & BROKEN_DEPEND)
  114. check_str(cfg.config.lpDependencies, depend, "Wrong dependencies");
  115. todo_wine_if(broken & BROKEN_DISPLAY_NAME)
  116. check_str(cfg.config.lpDisplayName, display_name, "Wrong display name");
  117. todo_wine_if(broken & BROKEN_DELAYED_AUTO_START)
  118. check_dw((DWORD)delayed_auto_info.fDelayedAutostart, (DWORD)delayed_auto, "Wrong delayed autostart value");
  119. #undef check_dw
  120. #undef check_str
  121. done:
  122. CloseServiceHandle(svc);
  123. }
  124. #define delete_service(n,e,b) delete_service_(__FILE__,__LINE__,n,e,b)
  125. static void delete_service_(const char *file, unsigned line, const char *name, DWORD expected_status, BOOL broken)
  126. {
  127. char command[256];
  128. BOOL bret;
  129. DWORD r;
  130. strcpy(command, "sc delete ");
  131. strcat(command, name);
  132. bret = run_sc_exe_(file, line, command, &r);
  133. lok(bret, "run_sc_exe failed\n");
  134. if (expected_status != SC_EXIT_SUCCESS && !strcmp(winetest_platform, "wine"))
  135. expected_status = 1;
  136. todo_wine_if(broken) lok(r == expected_status, "got exit code %ld, expected %ld\n", r, expected_status);
  137. }
  138. static void test_create_service(BOOL elevated)
  139. {
  140. static struct {
  141. const char *param;
  142. DWORD expected_start_type;
  143. DWORD expected_service_type;
  144. const char * expected_binary_path;
  145. DWORD broken;
  146. } start_types[] = {
  147. { "boot type= kernel", SERVICE_BOOT_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_BOOT,
  148. BROKEN_BINPATH | BROKEN_DISPLAY_NAME },
  149. { "system type= kernel", SERVICE_SYSTEM_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_SYSTEM,
  150. BROKEN_BINPATH | BROKEN_DISPLAY_NAME },
  151. { "auto", SERVICE_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY,
  152. BROKEN_DISPLAY_NAME },
  153. { "demand", SERVICE_DEMAND_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY,
  154. BROKEN_DISPLAY_NAME },
  155. { "disabled", SERVICE_DISABLED, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY,
  156. BROKEN_DISPLAY_NAME },
  157. { "delayed-auto", SERVICE_DELAYED_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY,
  158. BROKEN_START | BROKEN_DISPLAY_NAME | BROKEN_DELAYED_AUTO_START }
  159. };
  160. static struct {
  161. const char *param;
  162. DWORD expected_error_control;
  163. } error_severities[] = {
  164. { "normal", SERVICE_ERROR_NORMAL },
  165. { "severe", SERVICE_ERROR_SEVERE },
  166. { "critical", SERVICE_ERROR_CRITICAL },
  167. { "ignore", SERVICE_ERROR_IGNORE }
  168. };
  169. unsigned int i;
  170. DWORD r;
  171. if (!elevated)
  172. {
  173. win_skip("\"sc create\" tests need elevated permissions\n");
  174. return;
  175. }
  176. #define check_exit_code(x) ok(r == (x), "got exit code %ld, expected %d\n", r, (x))
  177. #define check_test_service(t,s,e,de,di,br) \
  178. check_service_definition(TEST_SERVICE_NAME, TEST_SERVICE_BINARY, t, s, e, de, di ? di : TEST_SERVICE_NAME, br)
  179. #define delete_test_service(x, y) \
  180. delete_service(TEST_SERVICE_NAME, (x) ? SC_EXIT_SUCCESS : SC_EXIT_SERVICE_DOES_NOT_EXIST, (y))
  181. #define check_test_service2(t,s,e,de,di,br) \
  182. check_service_definition(TEST_SERVICE_NAME2, TEST_SERVICE_BINARY, t, s, e, de, di ? di : TEST_SERVICE_NAME2, br)
  183. #define delete_test_service2(x, y) \
  184. delete_service(TEST_SERVICE_NAME2, (x) ? SC_EXIT_SUCCESS : SC_EXIT_SERVICE_DOES_NOT_EXIST, (y))
  185. /* too few parameters */
  186. run_sc_exe("sc create", &r);
  187. todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE);
  188. delete_test_service(FALSE, FALSE);
  189. run_sc_exe("sc create " TEST_SERVICE_NAME, &r);
  190. todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE);
  191. delete_test_service(FALSE, FALSE);
  192. /* binpath= */
  193. run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\"", &r);
  194. check_exit_code(SC_EXIT_SUCCESS);
  195. check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL,
  196. BROKEN_DISPLAY_NAME);
  197. /* existing service */
  198. run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" start= auto", &r);
  199. todo_wine check_exit_code(SC_EXIT_SERVICE_EXISTS);
  200. check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL,
  201. BROKEN_DISPLAY_NAME);
  202. delete_test_service(TRUE, FALSE);
  203. /* type= */
  204. run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" type= invalid", &r);
  205. todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE);
  206. delete_test_service(FALSE, TRUE);
  207. run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" type= own", &r);
  208. check_exit_code(SC_EXIT_SUCCESS);
  209. check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL,
  210. BROKEN_DISPLAY_NAME);
  211. delete_test_service(TRUE, FALSE);
  212. run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" type= interact", &r);
  213. todo_wine check_exit_code(SC_EXIT_INVALID_PARAMETER);
  214. delete_test_service(FALSE, TRUE);
  215. run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" type= interact type= own", &r);
  216. check_exit_code(SC_EXIT_SUCCESS);
  217. check_test_service(SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_DEMAND_START,
  218. SERVICE_ERROR_NORMAL, "", NULL, BROKEN_TYPE | BROKEN_DISPLAY_NAME);
  219. delete_test_service(TRUE, FALSE);
  220. /* start= */
  221. run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" start= invalid", &r);
  222. todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE);
  223. delete_test_service(FALSE, TRUE);
  224. for (i = 0; i < ARRAY_SIZE(start_types); i++)
  225. {
  226. char cmdline[256];
  227. strcpy(cmdline, "sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" start= ");
  228. strcat(cmdline, start_types[i].param);
  229. run_sc_exe(cmdline, &r);
  230. check_exit_code(SC_EXIT_SUCCESS);
  231. check_service_definition(TEST_SERVICE_NAME, start_types[i].expected_binary_path,
  232. start_types[i].expected_service_type, start_types[i].expected_start_type,
  233. SERVICE_ERROR_NORMAL, "", TEST_SERVICE_NAME, start_types[i].broken);
  234. delete_test_service(TRUE, FALSE);
  235. }
  236. /* error= */
  237. for (i = 0; i < ARRAY_SIZE(error_severities); i++)
  238. {
  239. char cmdline[256];
  240. strcpy(cmdline, "sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" error= ");
  241. strcat(cmdline, error_severities[i].param);
  242. run_sc_exe(cmdline, &r);
  243. check_exit_code(SC_EXIT_SUCCESS);
  244. check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START,
  245. error_severities[i].expected_error_control, "", NULL, BROKEN_DISPLAY_NAME);
  246. delete_test_service(TRUE, FALSE);
  247. }
  248. /* tag= */
  249. run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" tag= yes", &r);
  250. todo_wine check_exit_code(SC_EXIT_INVALID_PARAMETER);
  251. delete_test_service(FALSE, TRUE);
  252. run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" tag= no", &r);
  253. check_exit_code(SC_EXIT_SUCCESS);
  254. check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL,
  255. BROKEN_DISPLAY_NAME);
  256. delete_test_service(TRUE, FALSE);
  257. /* depend= */
  258. run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" depend= " TEST_SERVICE_NAME, &r);
  259. todo_wine check_exit_code(SC_EXIT_CIRCULAR_DEPENDENCY);
  260. delete_test_service(FALSE, TRUE);
  261. run_sc_exe("sc create " TEST_SERVICE_NAME2 " binpath= \"" TEST_SERVICE_BINARY "\" depend= " TEST_SERVICE_NAME, &r);
  262. check_exit_code(SC_EXIT_SUCCESS);
  263. check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
  264. TEST_SERVICE_NAME, NULL, BROKEN_DEPEND | BROKEN_DISPLAY_NAME);
  265. delete_test_service2(TRUE, FALSE);
  266. run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= " TEST_SERVICE_BINARY, &r);
  267. check_exit_code(SC_EXIT_SUCCESS);
  268. run_sc_exe("sc create " TEST_SERVICE_NAME2 " binpath= \"" TEST_SERVICE_BINARY "\" depend= " TEST_SERVICE_NAME, &r);
  269. check_exit_code(SC_EXIT_SUCCESS);
  270. check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
  271. TEST_SERVICE_NAME, NULL, BROKEN_DEPEND | BROKEN_DISPLAY_NAME);
  272. delete_test_service2(TRUE, FALSE);
  273. delete_test_service(TRUE, FALSE);
  274. /* displayname= */
  275. run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= " TEST_SERVICE_BINARY
  276. " displayname= \"Wine Test Service\"", &r);
  277. check_exit_code(SC_EXIT_SUCCESS);
  278. check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "",
  279. "Wine Test Service", 0);
  280. delete_test_service(TRUE, FALSE);
  281. /* without spaces */
  282. run_sc_exe("sc create " TEST_SERVICE_NAME2 " binpath=\"" TEST_SERVICE_BINARY "\" type=own start=auto"
  283. " error=normal tag=no depend=" TEST_SERVICE_NAME " displayname=\"Wine Test Service\"", &r);
  284. ok(r == SC_EXIT_SUCCESS || broken(r == SC_EXIT_INVALID_COMMAND_LINE), "got exit code %ld, expected %d\n",
  285. r, SC_EXIT_SUCCESS);
  286. if (r == SC_EXIT_SUCCESS)
  287. {
  288. check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
  289. TEST_SERVICE_NAME, "Wine Test Service", BROKEN_DEPEND);
  290. delete_test_service2(TRUE, FALSE);
  291. }
  292. else
  293. {
  294. delete_test_service2(FALSE, FALSE);
  295. }
  296. /* case-insensitive */
  297. run_sc_exe("SC CREATE " TEST_SERVICE_NAME2 " BINPATH= \"" TEST_SERVICE_BINARY "\" TYPE= OWN START= AUTO"
  298. " ERROR= NORMAL TAG= NO DEPEND= " TEST_SERVICE_NAME " DISPLAYNAME= \"Wine Test Service\"", &r);
  299. check_exit_code(SC_EXIT_SUCCESS);
  300. check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
  301. TEST_SERVICE_NAME, "Wine Test Service", BROKEN_DEPEND);
  302. delete_test_service2(TRUE, FALSE);
  303. #undef delete_test_service2
  304. #undef check_test_service2
  305. #undef delete_test_service
  306. #undef check_test_service
  307. #undef check_exit_code
  308. }
  309. /* taken from winetest, only whitespace changes */
  310. static int running_elevated(void)
  311. {
  312. HANDLE token;
  313. TOKEN_ELEVATION elevation_info;
  314. DWORD size;
  315. /* Get the process token */
  316. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
  317. return -1;
  318. /* Get the elevation info from the token */
  319. if (!GetTokenInformation(token, TokenElevation, &elevation_info, sizeof(TOKEN_ELEVATION), &size))
  320. {
  321. CloseHandle(token);
  322. return -1;
  323. }
  324. CloseHandle(token);
  325. return elevation_info.TokenIsElevated;
  326. }
  327. START_TEST(sc)
  328. {
  329. SECURITY_ATTRIBUTES secattr = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
  330. BOOL elevated = running_elevated();
  331. nul_file = CreateFileA("NUL", GENERIC_READ | GENERIC_WRITE, 0, &secattr, OPEN_EXISTING,
  332. FILE_ATTRIBUTE_NORMAL, NULL);
  333. scmgr = OpenSCManagerA(NULL, NULL, GENERIC_READ);
  334. ok(!!scmgr, "OpenSCManagerA failed: %ld\n", GetLastError());
  335. test_create_service(elevated);
  336. CloseServiceHandle(scmgr);
  337. CloseHandle(nul_file);
  338. }