Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- configure.ac | 1 + programs/sc/tests/Makefile.in | 5 + programs/sc/tests/sc.c | 402 ++++++++++++++++++++++++++++++++++ 3 files changed, 408 insertions(+) create mode 100644 programs/sc/tests/Makefile.in create mode 100644 programs/sc/tests/sc.c
diff --git a/configure.ac b/configure.ac index 74c80fd7fa8c..98f56fc849fa 100644 --- a/configure.ac +++ b/configure.ac @@ -3322,6 +3322,7 @@ WINE_CONFIG_MAKEFILE(programs/rpcss) WINE_CONFIG_MAKEFILE(programs/rundll.exe16,enable_win16) WINE_CONFIG_MAKEFILE(programs/rundll32) WINE_CONFIG_MAKEFILE(programs/sc) +WINE_CONFIG_MAKEFILE(programs/sc/tests) WINE_CONFIG_MAKEFILE(programs/schtasks) WINE_CONFIG_MAKEFILE(programs/schtasks/tests) WINE_CONFIG_MAKEFILE(programs/sdbinst) diff --git a/programs/sc/tests/Makefile.in b/programs/sc/tests/Makefile.in new file mode 100644 index 000000000000..24a875fb9051 --- /dev/null +++ b/programs/sc/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = sc.exe +IMPORTS = advapi32 + +C_SRCS = \ + sc.c diff --git a/programs/sc/tests/sc.c b/programs/sc/tests/sc.c new file mode 100644 index 000000000000..66493ed578c7 --- /dev/null +++ b/programs/sc/tests/sc.c @@ -0,0 +1,402 @@ +/* + * Copyright 2022 Torge Matthies for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <windows.h> +#include <winsvc.h> +#include <stdio.h> +#include "wine/test.h" + +#define lok ok_(__FILE__,line) + +#define TEST_SERVICE_NAME "wine_test_svc" +#define TEST_SERVICE_NAME2 "wine_test_svc_2" +#define TEST_SERVICE_BINARY "c:\windows\system32\cmd.exe" +#define TEST_SERVICE_BINARY_START_BOOT "\SystemRoot\system32\cmd.exe" +#define TEST_SERVICE_BINARY_START_SYSTEM "\??\" TEST_SERVICE_BINARY + +#define SC_EXIT_SUCCESS ERROR_SUCCESS +#define SC_EXIT_INVALID_PARAMETER ERROR_INVALID_PARAMETER +#define SC_EXIT_CIRCULAR_DEPENDENCY ERROR_CIRCULAR_DEPENDENCY +#define SC_EXIT_SERVICE_DOES_NOT_EXIST ERROR_SERVICE_DOES_NOT_EXIST +#define SC_EXIT_SERVICE_EXISTS ERROR_SERVICE_EXISTS +#define SC_EXIT_INVALID_COMMAND_LINE ERROR_INVALID_COMMAND_LINE + +static HANDLE nul_file; +static SC_HANDLE scmgr; + +/* Copied and modified from the reg.exe tests */ +#define run_sc_exe(c,r) run_sc_exe_(__FILE__,__LINE__,c,r) +static BOOL run_sc_exe_(const char *file, unsigned line, const char *cmd, DWORD *rc) +{ + STARTUPINFOA si = {sizeof(STARTUPINFOA)}; + PROCESS_INFORMATION pi; + BOOL bret; + DWORD ret; + char cmdline[256]; + + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = nul_file; + si.hStdOutput = nul_file; + si.hStdError = nul_file; + + strcpy(cmdline, cmd); + if (!CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) + return FALSE; + + ret = WaitForSingleObject(pi.hProcess, 10000); + if (ret == WAIT_TIMEOUT) + TerminateProcess(pi.hProcess, 1); + + bret = GetExitCodeProcess(pi.hProcess, rc); + lok(bret, "GetExitCodeProcess failed: %ld\n", GetLastError()); + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return bret; +} + +#define BROKEN_CREATE 0x000000001UL +#define BROKEN_BINPATH 0x000000002UL +#define BROKEN_TYPE 0x000000004UL +#define BROKEN_START 0x000000008UL +#define BROKEN_ERROR 0x000000010UL +#define BROKEN_DEPEND 0x000000020UL +#define BROKEN_DISPLAY_NAME 0x000000040UL +#define BROKEN_DELAYED_AUTO_START 0x000000080UL +#define BROKEN_ALL ~0UL + +#define SERVICE_DELAYED_AUTO_START (SERVICE_AUTO_START | 0x80000000) + +#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) +static void check_service_definition_(const char *file, unsigned line, char const *name, + const char *binpath, DWORD type, DWORD start, DWORD error, + const char *depend, const char *display_name, DWORD broken) +{ + SERVICE_DELAYED_AUTO_START_INFO delayed_auto_info = {0}; + union { + char buffer[8192]; + QUERY_SERVICE_CONFIGA config; + } cfg; + BOOL delayed_auto; + SC_HANDLE svc; + DWORD needed; + BOOL ret; + + delayed_auto = !!(start & 0x80000000); + start &= ~0x80000000; + + if (!scmgr) + return; + + svc = OpenServiceA(scmgr, name, GENERIC_READ); + todo_wine_if(broken & BROKEN_CREATE) + lok(!!svc, "OpenServiceA failed: %ld\n", GetLastError()); + if (!svc) + return; + + ret = QueryServiceConfigA(svc, &cfg.config, sizeof(cfg.buffer), &needed); + lok(!!ret, "QueryServiceConfigA failed: %ld\n", GetLastError()); + if (!ret) + goto done; + + ret = QueryServiceConfig2A(svc, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (LPBYTE)&delayed_auto_info, + sizeof(delayed_auto_info), &needed); + todo_wine lok(!!ret, "QueryServiceConfig2A(SERVICE_CONFIG_DELAYED_AUTO_START_INFO) failed: %ld\n", + GetLastError()); + +#define check_str(a, b, msg) lok((a) && (b) && (a) != (b) && !strcmp((a), (b)), msg ": %s != %s\n", \ + debugstr_a((a)), debugstr_a((b))) +#define check_dw(a, b, msg) lok((a) == (b), msg ": 0x%lx != 0x%lx\n", a, b) + + todo_wine_if(broken & BROKEN_BINPATH) + check_str(cfg.config.lpBinaryPathName, binpath, "Wrong binary path"); + todo_wine_if(broken & BROKEN_TYPE) + check_dw(cfg.config.dwServiceType, type, "Wrong service type"); + todo_wine_if(broken & BROKEN_START) + check_dw(cfg.config.dwStartType, start, "Wrong start type"); + todo_wine_if(broken & BROKEN_ERROR) + check_dw(cfg.config.dwErrorControl, error, "Wrong error control"); + todo_wine_if(broken & BROKEN_DEPEND) + check_str(cfg.config.lpDependencies, depend, "Wrong dependencies"); + todo_wine_if(broken & BROKEN_DISPLAY_NAME) + check_str(cfg.config.lpDisplayName, display_name, "Wrong display name"); + todo_wine_if(broken & BROKEN_DELAYED_AUTO_START) + check_dw((DWORD)delayed_auto_info.fDelayedAutostart, (DWORD)delayed_auto, "Wrong delayed autostart value"); + +#undef check_dw +#undef check_str + +done: + CloseServiceHandle(svc); +} + +#define delete_service(n,e,b) delete_service_(__FILE__,__LINE__,n,e,b) +static void delete_service_(const char *file, unsigned line, const char *name, DWORD expected_status, BOOL broken) +{ + char command[256]; + BOOL bret; + DWORD r; + + strcpy(command, "sc delete "); + strcat(command, name); + bret = run_sc_exe_(file, line, command, &r); + lok(bret, "run_sc_exe failed\n"); + if (expected_status != SC_EXIT_SUCCESS && !strcmp(winetest_platform, "wine")) + expected_status = 1; + todo_wine_if(broken) lok(r == expected_status, "got exit code %ld, expected %ld\n", r, expected_status); +} + +static void test_create_service(BOOL elevated) +{ + static struct { + const char *param; + DWORD expected_start_type; + DWORD expected_service_type; + const char * expected_binary_path; + } start_types[] = { + { "boot type= kernel", SERVICE_BOOT_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_BOOT }, + { "system type= kernel", SERVICE_SYSTEM_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_SYSTEM }, + { "auto", SERVICE_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY }, + { "demand", SERVICE_DEMAND_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY }, + { "disabled", SERVICE_DISABLED, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY }, + { "delayed-auto", SERVICE_DELAYED_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY } + }; + static struct { + const char *param; + DWORD expected_error_control; + } error_severities[] = { + { "normal", SERVICE_ERROR_NORMAL }, + { "severe", SERVICE_ERROR_SEVERE }, + { "critical", SERVICE_ERROR_CRITICAL }, + { "ignore", SERVICE_ERROR_IGNORE } + }; + unsigned int i; + DWORD r; + + if (!elevated) + { + win_skip(""sc create" tests need elevated permissions\n"); + return; + } + +#define check_exit_code(x) ok(r == (x), "got exit code %ld, expected %d\n", r, (x)) +#define check_test_service(t,s,e,de,di,br) \ + check_service_definition(TEST_SERVICE_NAME, TEST_SERVICE_BINARY, t, s, e, de, di ? di : TEST_SERVICE_NAME, br) +#define delete_test_service(x, y) \ + delete_service(TEST_SERVICE_NAME, (x) ? SC_EXIT_SUCCESS : SC_EXIT_SERVICE_DOES_NOT_EXIST, (y)) +#define check_test_service2(t,s,e,de,di,br) \ + check_service_definition(TEST_SERVICE_NAME2, TEST_SERVICE_BINARY, t, s, e, de, di ? di : TEST_SERVICE_NAME2, br) +#define delete_test_service2(x, y) \ + delete_service(TEST_SERVICE_NAME2, (x) ? SC_EXIT_SUCCESS : SC_EXIT_SERVICE_DOES_NOT_EXIST, (y)) + + /* too few parameters */ + + run_sc_exe("sc create", &r); + todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE); + delete_test_service(FALSE, FALSE); + + run_sc_exe("sc create " TEST_SERVICE_NAME, &r); + todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE); + delete_test_service(FALSE, FALSE); + + /* binpath= */ + + run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY """, &r); + todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL, + BROKEN_CREATE); + + /* existing service */ + + run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" start= auto", &r); + todo_wine check_exit_code(SC_EXIT_SERVICE_EXISTS); + check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL, + BROKEN_CREATE); + delete_test_service(TRUE, TRUE); + + /* type= */ + + run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" type= invalid", &r); + todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE); + delete_test_service(FALSE, FALSE); + + run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" type= own", &r); + todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL, + BROKEN_CREATE); + delete_test_service(TRUE, TRUE); + + run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" type= interact", &r); + todo_wine check_exit_code(SC_EXIT_INVALID_PARAMETER); + delete_test_service(FALSE, FALSE); + + run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" type= interact type= own", &r); + todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_test_service(SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, "", NULL, BROKEN_CREATE); + delete_test_service(TRUE, TRUE); + + /* start= */ + + run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" start= invalid", &r); + todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE); + delete_test_service(FALSE, FALSE); + + for (i = 0; i < ARRAY_SIZE(start_types); i++) + { + char cmdline[256]; + + strcpy(cmdline, "sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" start= "); + strcat(cmdline, start_types[i].param); + run_sc_exe(cmdline, &r); + todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_service_definition(TEST_SERVICE_NAME, start_types[i].expected_binary_path, + start_types[i].expected_service_type, start_types[i].expected_start_type, + SERVICE_ERROR_NORMAL, "", TEST_SERVICE_NAME, BROKEN_CREATE); + delete_test_service(TRUE, TRUE); + } + + /* error= */ + + for (i = 0; i < ARRAY_SIZE(error_severities); i++) + { + char cmdline[256]; + + strcpy(cmdline, "sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" error= "); + strcat(cmdline, error_severities[i].param); + run_sc_exe(cmdline, &r); + todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, + error_severities[i].expected_error_control, "", NULL, BROKEN_CREATE); + delete_test_service(TRUE, TRUE); + } + + /* tag= */ + + run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" tag= yes", &r); + todo_wine check_exit_code(SC_EXIT_INVALID_PARAMETER); + delete_test_service(FALSE, FALSE); + + run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" tag= no", &r); + todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL, + BROKEN_CREATE); + delete_test_service(TRUE, TRUE); + + /* depend= */ + + run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" depend= " TEST_SERVICE_NAME, &r); + todo_wine check_exit_code(SC_EXIT_CIRCULAR_DEPENDENCY); + delete_test_service(FALSE, FALSE); + + run_sc_exe("sc create " TEST_SERVICE_NAME2 " binpath= "" TEST_SERVICE_BINARY "" depend= " TEST_SERVICE_NAME, &r); + todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, + TEST_SERVICE_NAME, NULL, BROKEN_CREATE); + delete_test_service2(TRUE, TRUE); + + run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= " TEST_SERVICE_BINARY, &r); + todo_wine check_exit_code(SC_EXIT_SUCCESS); + run_sc_exe("sc create " TEST_SERVICE_NAME2 " binpath= "" TEST_SERVICE_BINARY "" depend= " TEST_SERVICE_NAME, &r); + todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, + TEST_SERVICE_NAME, NULL, BROKEN_CREATE); + delete_test_service2(TRUE, TRUE); + delete_test_service(TRUE, TRUE); + + /* displayname= */ + + run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= " TEST_SERVICE_BINARY + " displayname= "Wine Test Service"", &r); + todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", + "Wine Test Service", BROKEN_CREATE); + delete_test_service(TRUE, TRUE); + + /* without spaces */ + + run_sc_exe("sc create " TEST_SERVICE_NAME2 " binpath="" TEST_SERVICE_BINARY "" type=own start=auto" + " error=normal tag=no depend=" TEST_SERVICE_NAME " displayname="Wine Test Service"", &r); + ok(r == SC_EXIT_SUCCESS || broken(r == SC_EXIT_INVALID_COMMAND_LINE), "got exit code %ld, expected %d\n", + r, SC_EXIT_SUCCESS); + if (r == SC_EXIT_SUCCESS) + { + check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, + TEST_SERVICE_NAME, "Wine Test Service", BROKEN_DEPEND); + delete_test_service2(TRUE, FALSE); + } + else + { + delete_test_service2(FALSE, FALSE); + } + + /* case-insensitive */ + + run_sc_exe("SC CREATE " TEST_SERVICE_NAME2 " BINPATH= "" TEST_SERVICE_BINARY "" TYPE= OWN START= AUTO" + " ERROR= NORMAL TAG= NO DEPEND= " TEST_SERVICE_NAME " DISPLAYNAME= "Wine Test Service"", &r); + todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, + TEST_SERVICE_NAME, "Wine Test Service", BROKEN_CREATE); + delete_test_service2(TRUE, TRUE); + +#undef delete_test_service2 +#undef check_test_service2 +#undef delete_test_service +#undef check_test_service +#undef check_exit_code +} + +/* taken from winetest, only whitespace changes */ +static int running_elevated(void) +{ + HANDLE token; + TOKEN_ELEVATION elevation_info; + DWORD size; + + /* Get the process token */ + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) + return -1; + + /* Get the elevation info from the token */ + if (!GetTokenInformation(token, TokenElevation, &elevation_info, sizeof(TOKEN_ELEVATION), &size)) + { + CloseHandle(token); + return -1; + } + CloseHandle(token); + + return elevation_info.TokenIsElevated; +} + + +START_TEST(sc) +{ + SECURITY_ATTRIBUTES secattr = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE}; + BOOL elevated = running_elevated(); + + nul_file = CreateFileA("NUL", GENERIC_READ | GENERIC_WRITE, 0, &secattr, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + + scmgr = OpenSCManagerA(NULL, NULL, GENERIC_READ); + ok(!!scmgr, "OpenSCManagerA failed: %ld\n", GetLastError()); + + test_create_service(elevated); + + CloseServiceHandle(scmgr); + CloseHandle(nul_file); +}
In addition to using one argument for both parameter name and value.
This fixes a regression from commit 8b38c91d83844dea882922055ced7cdeb79c1693.
Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- programs/sc/sc.c | 101 ++++++++++++++++++++++++++++------------- programs/sc/tests/sc.c | 95 ++++++++++++++++++++------------------ 2 files changed, 120 insertions(+), 76 deletions(-)
diff --git a/programs/sc/sc.c b/programs/sc/sc.c index 43148ab99d60..bd7e093b61d2 100644 --- a/programs/sc/sc.c +++ b/programs/sc/sc.c @@ -27,6 +27,26 @@
WINE_DEFAULT_DEBUG_CHANNEL(sc);
+static BOOL parse_string_param( int argc, const WCHAR *argv[], unsigned int *index, + const WCHAR *param_name, size_t name_len, const WCHAR **out ) +{ + if (!wcsnicmp( argv[*index], param_name, name_len )) + { + if (argv[*index][name_len]) + { + *out = &argv[*index][name_len]; + return TRUE; + } + else if (*index < argc - 1) + { + *index += 1; + *out = argv[*index]; + return TRUE; + } + } + return FALSE; +} + struct create_params { const WCHAR *displayname; @@ -56,48 +76,56 @@ static BOOL parse_create_params( int argc, const WCHAR *argv[], struct create_pa cp->obj = NULL; cp->password = NULL;
+#define PARSE(x, y) parse_string_param( (argc), (argv), &(i), (x), ARRAY_SIZE(x) - 1, (y) ) for (i = 0; i < argc; i++) { - if (!wcsnicmp( argv[i], L"displayname=", 12 )) cp->displayname = argv[i] + 12; - if (!wcsnicmp( argv[i], L"binpath=", 8 )) cp->binpath = argv[i] + 8; - if (!wcsnicmp( argv[i], L"group=", 6 )) cp->group = argv[i] + 6; - if (!wcsnicmp( argv[i], L"depend=", 7 )) cp->depend = argv[i] + 7; - if (!wcsnicmp( argv[i], L"obj=", 4 )) cp->obj = argv[i] + 4; - if (!wcsnicmp( argv[i], L"password=", 9 )) cp->password = argv[i] + 9; - - if (!wcsnicmp( argv[i], L"tag=", 4 )) + const WCHAR *tag, *type, *start, *error; + + if (PARSE( L"displayname=", &cp->displayname )) continue; + if (PARSE( L"binpath=", &cp->binpath )) continue; + if (PARSE( L"group=", &cp->group )) continue; + if (PARSE( L"depend=", &cp->depend )) continue; + if (PARSE( L"obj=", &cp->obj )) continue; + if (PARSE( L"password=", &cp->password )) continue; + + if (PARSE( L"tag=", &tag )) { - if (!wcsicmp( argv[i] + 4, L"yes" )) + if (!wcsicmp( tag, L"yes" )) { WINE_FIXME("tag argument not supported\n"); cp->tag = TRUE; } + continue; } - if (!wcsnicmp( argv[i], L"type=", 5 )) + if (PARSE( L"type=", &type )) { - if (!wcsicmp( argv[i] + 5, L"own" )) cp->type = SERVICE_WIN32_OWN_PROCESS; - if (!wcsicmp( argv[i] + 5, L"share" )) cp->type = SERVICE_WIN32_SHARE_PROCESS; - if (!wcsicmp( argv[i] + 5, L"kernel" )) cp->type = SERVICE_KERNEL_DRIVER; - if (!wcsicmp( argv[i] + 5, L"filesys" )) cp->type = SERVICE_FILE_SYSTEM_DRIVER; - if (!wcsicmp( argv[i] + 5, L"rec" )) cp->type = SERVICE_RECOGNIZER_DRIVER; - if (!wcsicmp( argv[i] + 5, L"interact" )) cp->type |= SERVICE_INTERACTIVE_PROCESS; + if (!wcsicmp( type, L"own" )) cp->type = SERVICE_WIN32_OWN_PROCESS; + else if (!wcsicmp( type, L"share" )) cp->type = SERVICE_WIN32_SHARE_PROCESS; + else if (!wcsicmp( type, L"kernel" )) cp->type = SERVICE_KERNEL_DRIVER; + else if (!wcsicmp( type, L"filesys" )) cp->type = SERVICE_FILE_SYSTEM_DRIVER; + else if (!wcsicmp( type, L"rec" )) cp->type = SERVICE_RECOGNIZER_DRIVER; + else if (!wcsicmp( type, L"interact" )) cp->type |= SERVICE_INTERACTIVE_PROCESS; + continue; } - if (!wcsnicmp( argv[i], L"start=", 6 )) + if (PARSE( L"start=", &start )) { - if (!wcsicmp( argv[i] + 6, L"boot" )) cp->start = SERVICE_BOOT_START; - if (!wcsicmp( argv[i] + 6, L"system" )) cp->start = SERVICE_SYSTEM_START; - if (!wcsicmp( argv[i] + 6, L"auto" )) cp->start = SERVICE_AUTO_START; - if (!wcsicmp( argv[i] + 6, L"demand" )) cp->start = SERVICE_DEMAND_START; - if (!wcsicmp( argv[i] + 6, L"disabled" )) cp->start = SERVICE_DISABLED; + if (!wcsicmp( start, L"boot" )) cp->start = SERVICE_BOOT_START; + else if (!wcsicmp( start, L"system" )) cp->start = SERVICE_SYSTEM_START; + else if (!wcsicmp( start, L"auto" )) cp->start = SERVICE_AUTO_START; + else if (!wcsicmp( start, L"demand" )) cp->start = SERVICE_DEMAND_START; + else if (!wcsicmp( start, L"disabled" )) cp->start = SERVICE_DISABLED; + continue; } - if (!wcsnicmp( argv[i], L"error=", 6 )) + if (PARSE( L"error=", &error )) { - if (!wcsicmp( argv[i] + 6, L"normal" )) cp->error = SERVICE_ERROR_NORMAL; - if (!wcsicmp( argv[i] + 6, L"severe" )) cp->error = SERVICE_ERROR_SEVERE; - if (!wcsicmp( argv[i] + 6, L"critical" )) cp->error = SERVICE_ERROR_CRITICAL; - if (!wcsicmp( argv[i] + 6, L"ignore" )) cp->error = SERVICE_ERROR_IGNORE; + if (!wcsicmp( error, L"normal" )) cp->error = SERVICE_ERROR_NORMAL; + else if (!wcsicmp( error, L"severe" )) cp->error = SERVICE_ERROR_SEVERE; + else if (!wcsicmp( error, L"critical" )) cp->error = SERVICE_ERROR_CRITICAL; + else if (!wcsicmp( error, L"ignore" )) cp->error = SERVICE_ERROR_IGNORE; + continue; } } +#undef PARSE if (!cp->binpath) return FALSE; return TRUE; } @@ -156,16 +184,25 @@ static BOOL parse_failure_params( int argc, const WCHAR *argv[], SERVICE_FAILURE fa->cActions = 0; fa->lpsaActions = NULL;
+#define PARSE(x, y) parse_string_param( (argc), (argv), &(i), (x), ARRAY_SIZE(x) - 1, (y) ) for (i = 0; i < argc; i++) { - if (!wcsnicmp( argv[i], L"reset=", 6 )) fa->dwResetPeriod = wcstol( argv[i] + 6, NULL, 10 ); - if (!wcsnicmp( argv[i], L"reboot=", 7 )) fa->lpRebootMsg = (WCHAR *)argv[i] + 7; - if (!wcsnicmp( argv[i], L"command=", 8 )) fa->lpCommand = (WCHAR *)argv[i] + 8; - if (!wcsnicmp( argv[i], L"actions=", 8 )) + const WCHAR *reset, *actions; + + if (PARSE( L"reset=", &reset )) + { + fa->dwResetPeriod = wcstol( reset, NULL, 10 ); + continue; + } + if (PARSE( L"reboot=", (const WCHAR **)&fa->lpRebootMsg )) continue; + if (PARSE( L"command=", (const WCHAR **)&fa->lpCommand )) continue; + if (PARSE( L"actions=", &actions )) { - if (!parse_failure_actions( argv[i] + 8, fa )) return FALSE; + if (!parse_failure_actions( actions, fa )) return FALSE; + continue; } } +#undef PARSE return TRUE; }
diff --git a/programs/sc/tests/sc.c b/programs/sc/tests/sc.c index 66493ed578c7..57046905a4b0 100644 --- a/programs/sc/tests/sc.c +++ b/programs/sc/tests/sc.c @@ -168,13 +168,20 @@ static void test_create_service(BOOL elevated) DWORD expected_start_type; DWORD expected_service_type; const char * expected_binary_path; + DWORD broken; } start_types[] = { - { "boot type= kernel", SERVICE_BOOT_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_BOOT }, - { "system type= kernel", SERVICE_SYSTEM_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_SYSTEM }, - { "auto", SERVICE_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY }, - { "demand", SERVICE_DEMAND_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY }, - { "disabled", SERVICE_DISABLED, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY }, - { "delayed-auto", SERVICE_DELAYED_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY } + { "boot type= kernel", SERVICE_BOOT_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_BOOT, + BROKEN_BINPATH | BROKEN_DISPLAY_NAME }, + { "system type= kernel", SERVICE_SYSTEM_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_SYSTEM, + BROKEN_BINPATH | BROKEN_DISPLAY_NAME }, + { "auto", SERVICE_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY, + BROKEN_DISPLAY_NAME }, + { "demand", SERVICE_DEMAND_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY, + BROKEN_DISPLAY_NAME }, + { "disabled", SERVICE_DISABLED, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY, + BROKEN_DISPLAY_NAME }, + { "delayed-auto", SERVICE_DELAYED_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY, + BROKEN_START | BROKEN_DISPLAY_NAME | BROKEN_DELAYED_AUTO_START } }; static struct { const char *param; @@ -217,45 +224,45 @@ static void test_create_service(BOOL elevated) /* binpath= */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY """, &r); - todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_exit_code(SC_EXIT_SUCCESS); check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL, - BROKEN_CREATE); + BROKEN_DISPLAY_NAME);
/* existing service */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" start= auto", &r); todo_wine check_exit_code(SC_EXIT_SERVICE_EXISTS); check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL, - BROKEN_CREATE); - delete_test_service(TRUE, TRUE); + BROKEN_DISPLAY_NAME); + delete_test_service(TRUE, FALSE);
/* type= */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" type= invalid", &r); todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE); - delete_test_service(FALSE, FALSE); + delete_test_service(FALSE, TRUE);
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" type= own", &r); - todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_exit_code(SC_EXIT_SUCCESS); check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL, - BROKEN_CREATE); - delete_test_service(TRUE, TRUE); + BROKEN_DISPLAY_NAME); + delete_test_service(TRUE, FALSE);
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" type= interact", &r); todo_wine check_exit_code(SC_EXIT_INVALID_PARAMETER); - delete_test_service(FALSE, FALSE); + delete_test_service(FALSE, TRUE);
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" type= interact type= own", &r); - todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_exit_code(SC_EXIT_SUCCESS); check_test_service(SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_DEMAND_START, - SERVICE_ERROR_NORMAL, "", NULL, BROKEN_CREATE); - delete_test_service(TRUE, TRUE); + SERVICE_ERROR_NORMAL, "", NULL, BROKEN_TYPE | BROKEN_DISPLAY_NAME); + delete_test_service(TRUE, FALSE);
/* start= */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" start= invalid", &r); todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE); - delete_test_service(FALSE, FALSE); + delete_test_service(FALSE, TRUE);
for (i = 0; i < ARRAY_SIZE(start_types); i++) { @@ -264,11 +271,11 @@ static void test_create_service(BOOL elevated) strcpy(cmdline, "sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" start= "); strcat(cmdline, start_types[i].param); run_sc_exe(cmdline, &r); - todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_exit_code(SC_EXIT_SUCCESS); check_service_definition(TEST_SERVICE_NAME, start_types[i].expected_binary_path, start_types[i].expected_service_type, start_types[i].expected_start_type, - SERVICE_ERROR_NORMAL, "", TEST_SERVICE_NAME, BROKEN_CREATE); - delete_test_service(TRUE, TRUE); + SERVICE_ERROR_NORMAL, "", TEST_SERVICE_NAME, start_types[i].broken); + delete_test_service(TRUE, FALSE); }
/* error= */ @@ -280,53 +287,53 @@ static void test_create_service(BOOL elevated) strcpy(cmdline, "sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" error= "); strcat(cmdline, error_severities[i].param); run_sc_exe(cmdline, &r); - todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_exit_code(SC_EXIT_SUCCESS); check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, - error_severities[i].expected_error_control, "", NULL, BROKEN_CREATE); - delete_test_service(TRUE, TRUE); + error_severities[i].expected_error_control, "", NULL, BROKEN_DISPLAY_NAME); + delete_test_service(TRUE, FALSE); }
/* tag= */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" tag= yes", &r); todo_wine check_exit_code(SC_EXIT_INVALID_PARAMETER); - delete_test_service(FALSE, FALSE); + delete_test_service(FALSE, TRUE);
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" tag= no", &r); - todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_exit_code(SC_EXIT_SUCCESS); check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL, - BROKEN_CREATE); - delete_test_service(TRUE, TRUE); + BROKEN_DISPLAY_NAME); + delete_test_service(TRUE, FALSE);
/* depend= */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= "" TEST_SERVICE_BINARY "" depend= " TEST_SERVICE_NAME, &r); todo_wine check_exit_code(SC_EXIT_CIRCULAR_DEPENDENCY); - delete_test_service(FALSE, FALSE); + delete_test_service(FALSE, TRUE);
run_sc_exe("sc create " TEST_SERVICE_NAME2 " binpath= "" TEST_SERVICE_BINARY "" depend= " TEST_SERVICE_NAME, &r); - todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_exit_code(SC_EXIT_SUCCESS); check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, - TEST_SERVICE_NAME, NULL, BROKEN_CREATE); - delete_test_service2(TRUE, TRUE); + TEST_SERVICE_NAME, NULL, BROKEN_DEPEND | BROKEN_DISPLAY_NAME); + delete_test_service2(TRUE, FALSE);
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= " TEST_SERVICE_BINARY, &r); - todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_exit_code(SC_EXIT_SUCCESS); run_sc_exe("sc create " TEST_SERVICE_NAME2 " binpath= "" TEST_SERVICE_BINARY "" depend= " TEST_SERVICE_NAME, &r); - todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_exit_code(SC_EXIT_SUCCESS); check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, - TEST_SERVICE_NAME, NULL, BROKEN_CREATE); - delete_test_service2(TRUE, TRUE); - delete_test_service(TRUE, TRUE); + TEST_SERVICE_NAME, NULL, BROKEN_DEPEND | BROKEN_DISPLAY_NAME); + delete_test_service2(TRUE, FALSE); + delete_test_service(TRUE, FALSE);
/* displayname= */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= " TEST_SERVICE_BINARY " displayname= "Wine Test Service"", &r); - todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_exit_code(SC_EXIT_SUCCESS); check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", - "Wine Test Service", BROKEN_CREATE); - delete_test_service(TRUE, TRUE); + "Wine Test Service", 0); + delete_test_service(TRUE, FALSE);
/* without spaces */
@@ -349,10 +356,10 @@ static void test_create_service(BOOL elevated)
run_sc_exe("SC CREATE " TEST_SERVICE_NAME2 " BINPATH= "" TEST_SERVICE_BINARY "" TYPE= OWN START= AUTO" " ERROR= NORMAL TAG= NO DEPEND= " TEST_SERVICE_NAME " DISPLAYNAME= "Wine Test Service"", &r); - todo_wine check_exit_code(SC_EXIT_SUCCESS); + check_exit_code(SC_EXIT_SUCCESS); check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, - TEST_SERVICE_NAME, "Wine Test Service", BROKEN_CREATE); - delete_test_service2(TRUE, TRUE); + TEST_SERVICE_NAME, "Wine Test Service", BROKEN_DEPEND); + delete_test_service2(TRUE, FALSE);
#undef delete_test_service2 #undef check_test_service2