svchost: Implementation of svchost.
This commit is contained in:
parent
dfc0564993
commit
4714c4fa80
|
@ -676,6 +676,7 @@ programs/rundll32/rundll32
|
|||
programs/spoolsv/spoolsv
|
||||
programs/start/rsrc.res
|
||||
programs/start/start
|
||||
programs/svchost/svchost
|
||||
programs/taskmgr/taskmgr
|
||||
programs/taskmgr/taskmgr.res
|
||||
programs/uninstaller/rsrc.res
|
||||
|
|
|
@ -491,6 +491,7 @@ ALL_MAKEFILES = \
|
|||
programs/rundll32/Makefile \
|
||||
programs/spoolsv/Makefile \
|
||||
programs/start/Makefile \
|
||||
programs/svchost/Makefile \
|
||||
programs/taskmgr/Makefile \
|
||||
programs/uninstaller/Makefile \
|
||||
programs/view/Makefile \
|
||||
|
@ -871,6 +872,7 @@ programs/rpcss/Makefile: programs/rpcss/Makefile.in programs/Makeprog.rules
|
|||
programs/rundll32/Makefile: programs/rundll32/Makefile.in programs/Makeprog.rules
|
||||
programs/spoolsv/Makefile: programs/spoolsv/Makefile.in programs/Makeprog.rules
|
||||
programs/start/Makefile: programs/start/Makefile.in programs/Makeprog.rules
|
||||
programs/svchost/Makefile: programs/svchost/Makefile.in programs/Makeprog.rules
|
||||
programs/taskmgr/Makefile: programs/taskmgr/Makefile.in programs/Makeprog.rules
|
||||
programs/uninstaller/Makefile: programs/uninstaller/Makefile.in programs/Makeprog.rules
|
||||
programs/view/Makefile: programs/view/Makefile.in programs/Makeprog.rules
|
||||
|
|
|
@ -21191,6 +21191,8 @@ ac_config_files="$ac_config_files programs/spoolsv/Makefile"
|
|||
|
||||
ac_config_files="$ac_config_files programs/start/Makefile"
|
||||
|
||||
ac_config_files="$ac_config_files programs/svchost/Makefile"
|
||||
|
||||
ac_config_files="$ac_config_files programs/taskmgr/Makefile"
|
||||
|
||||
ac_config_files="$ac_config_files programs/uninstaller/Makefile"
|
||||
|
@ -22154,6 +22156,7 @@ do
|
|||
"programs/rundll32/Makefile") CONFIG_FILES="$CONFIG_FILES programs/rundll32/Makefile" ;;
|
||||
"programs/spoolsv/Makefile") CONFIG_FILES="$CONFIG_FILES programs/spoolsv/Makefile" ;;
|
||||
"programs/start/Makefile") CONFIG_FILES="$CONFIG_FILES programs/start/Makefile" ;;
|
||||
"programs/svchost/Makefile") CONFIG_FILES="$CONFIG_FILES programs/svchost/Makefile" ;;
|
||||
"programs/taskmgr/Makefile") CONFIG_FILES="$CONFIG_FILES programs/taskmgr/Makefile" ;;
|
||||
"programs/uninstaller/Makefile") CONFIG_FILES="$CONFIG_FILES programs/uninstaller/Makefile" ;;
|
||||
"programs/view/Makefile") CONFIG_FILES="$CONFIG_FILES programs/view/Makefile" ;;
|
||||
|
|
|
@ -1912,6 +1912,7 @@ AC_CONFIG_FILES([programs/rpcss/Makefile])
|
|||
AC_CONFIG_FILES([programs/rundll32/Makefile])
|
||||
AC_CONFIG_FILES([programs/spoolsv/Makefile])
|
||||
AC_CONFIG_FILES([programs/start/Makefile])
|
||||
AC_CONFIG_FILES([programs/svchost/Makefile])
|
||||
AC_CONFIG_FILES([programs/taskmgr/Makefile])
|
||||
AC_CONFIG_FILES([programs/uninstaller/Makefile])
|
||||
AC_CONFIG_FILES([programs/view/Makefile])
|
||||
|
|
|
@ -27,6 +27,7 @@ SUBDIRS = \
|
|||
rundll32 \
|
||||
spoolsv \
|
||||
start \
|
||||
svchost \
|
||||
taskmgr \
|
||||
uninstaller \
|
||||
view \
|
||||
|
@ -70,6 +71,7 @@ INSTALLSUBDIRS = \
|
|||
rundll32 \
|
||||
spoolsv \
|
||||
start \
|
||||
svchost \
|
||||
taskmgr \
|
||||
uninstaller \
|
||||
wineboot \
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
TOPSRCDIR = @top_srcdir@
|
||||
TOPOBJDIR = ../..
|
||||
SRCDIR = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
MODULE = svchost.exe
|
||||
APPMODE = -municode
|
||||
IMPORTS = advapi32 kernel32
|
||||
|
||||
C_SRCS = \
|
||||
svchost.c
|
||||
|
||||
@MAKE_PROG_RULES@
|
||||
|
||||
@DEPENDENCIES@ # everything below this line is overwritten by make depend
|
|
@ -0,0 +1,368 @@
|
|||
/*
|
||||
* Implementation of svchost.exe
|
||||
*
|
||||
* Copyright 2007 Google (Roy Shea)
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* Usage:
|
||||
* Starting a service group:
|
||||
*
|
||||
* svchost /k service_group_name
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "windef.h"
|
||||
#include "winbase.h"
|
||||
#include "winreg.h"
|
||||
#include "winsvc.h"
|
||||
#include "wine/debug.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(svchost);
|
||||
|
||||
/* Static strings used throughout svchost */
|
||||
static const WCHAR kd[] = {'-','k',0};
|
||||
|
||||
static const WCHAR ks[] = {'/','k',0};
|
||||
|
||||
static const WCHAR reg_separator[] = {'\\',0};
|
||||
|
||||
static const WCHAR service_reg_path[] = {
|
||||
'S','y','s','t','e','m',
|
||||
'\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
|
||||
'\\','S','e','r','v','i','c','e','s',0};
|
||||
|
||||
static const WCHAR parameters[] = {
|
||||
'P','a','r','a','m','e','t','e','r','s',0};
|
||||
|
||||
static const WCHAR service_dll[] = {
|
||||
'S','e','r','v','i','c','e','D','l','l',0};
|
||||
|
||||
static const WCHAR svchost_path[] = {
|
||||
'S','o','f','t','w','a','r','e',
|
||||
'\\','M','i','c','r','o','s','o','f','t',
|
||||
'\\','W','i','n','d','o','w','s',' ','N','T',
|
||||
'\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
|
||||
'\\','S','v','c','h','o','s','t',0};
|
||||
|
||||
static const CHAR service_main[] = "ServiceMain";
|
||||
|
||||
/* Allocate and initialize a WSTR containing the queried value */
|
||||
static LPWSTR GetRegValue(HKEY service_key, const WCHAR *value_name)
|
||||
{
|
||||
DWORD type;
|
||||
DWORD reg_size;
|
||||
DWORD size;
|
||||
LONG ret;
|
||||
LPWSTR value;
|
||||
|
||||
WINE_TRACE("\n");
|
||||
|
||||
ret = RegQueryValueExW(service_key, value_name, NULL, &type, NULL, ®_size);
|
||||
if (ret != ERROR_SUCCESS)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add space for potentially missing NULL terminators in initial alloc.
|
||||
* The worst case REG_MULTI_SZ requires two NULL terminators. */
|
||||
size = reg_size + (2 * sizeof(WCHAR));
|
||||
value = HeapAlloc(GetProcessHeap(), 0, size);
|
||||
|
||||
ret = RegQueryValueExW(service_key, value_name, NULL, &type,
|
||||
(LPBYTE)value, ®_size);
|
||||
if (ret != ERROR_SUCCESS)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Explicitly NULL terminate the result */
|
||||
value[size / sizeof(WCHAR) - 1] = '\0';
|
||||
value[size / sizeof(WCHAR) - 2] = '\0';
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Allocate and initialize a WSTR containing the expanded string */
|
||||
static LPWSTR ExpandEnv(LPWSTR string)
|
||||
{
|
||||
DWORD size;
|
||||
LPWSTR expanded_string;
|
||||
|
||||
WINE_TRACE("\n");
|
||||
|
||||
size = 0;
|
||||
size = ExpandEnvironmentStringsW(string, NULL, size);
|
||||
if (size == 0)
|
||||
{
|
||||
WINE_ERR("cannot expand env vars in %s: %u\n",
|
||||
wine_dbgstr_w(string), GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
expanded_string = HeapAlloc(GetProcessHeap(), 0,
|
||||
(size + 1) * sizeof(WCHAR));
|
||||
if (ExpandEnvironmentStringsW(string, expanded_string, size) == 0)
|
||||
{
|
||||
WINE_ERR("cannot expand env vars in %s: %u\n",
|
||||
wine_dbgstr_w(string), GetLastError());
|
||||
HeapFree(GetProcessHeap(), 0, expanded_string);
|
||||
return NULL;
|
||||
}
|
||||
return expanded_string;
|
||||
}
|
||||
|
||||
/* Fill in service table entry for a specified service */
|
||||
static BOOL AddServiceElem(LPWSTR service_name,
|
||||
SERVICE_TABLE_ENTRYW *service_table_entry)
|
||||
{
|
||||
LONG ret;
|
||||
HKEY service_hkey = NULL;
|
||||
LPWSTR service_param_key = NULL;
|
||||
LPWSTR dll_name_short = NULL;
|
||||
LPWSTR dll_name_long = NULL;
|
||||
LPSTR dll_service_main = NULL;
|
||||
HMODULE library = NULL;
|
||||
LPSERVICE_MAIN_FUNCTIONW service_main_func = NULL;
|
||||
BOOL success = FALSE;
|
||||
DWORD reg_size;
|
||||
DWORD size;
|
||||
|
||||
WINE_TRACE("Adding element for %s\n", wine_dbgstr_w(service_name));
|
||||
|
||||
/* Construct registry path to the service's parameters key */
|
||||
size = (lstrlenW(service_reg_path) + lstrlenW(reg_separator) +
|
||||
lstrlenW(service_name) + lstrlenW(reg_separator) +
|
||||
lstrlenW(parameters) + 1);
|
||||
service_param_key = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
|
||||
lstrcpyW(service_param_key, service_reg_path);
|
||||
lstrcatW(service_param_key, reg_separator);
|
||||
lstrcatW(service_param_key, service_name);
|
||||
lstrcatW(service_param_key, reg_separator);
|
||||
lstrcatW(service_param_key, parameters);
|
||||
service_param_key[size - 1] = '\0';
|
||||
ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, service_param_key, 0,
|
||||
KEY_READ, &service_hkey);
|
||||
if (ret != ERROR_SUCCESS)
|
||||
{
|
||||
WINE_ERR("cannot open key %s, err=%d\n",
|
||||
wine_dbgstr_w(service_param_key), ret);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Find DLL associate with service from key */
|
||||
dll_name_short = GetRegValue(service_hkey, service_dll);
|
||||
if (!dll_name_short)
|
||||
{
|
||||
WINE_ERR("cannot find registry value %s for service %s\n",
|
||||
wine_dbgstr_w(service_dll), wine_dbgstr_w(service_name));
|
||||
RegCloseKey(service_hkey);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Expand environment variables in ServiceDll name*/
|
||||
dll_name_long = ExpandEnv(dll_name_short);
|
||||
if (!dll_name_long)
|
||||
{
|
||||
WINE_ERR("failed to expand string %s\n",
|
||||
wine_dbgstr_w(dll_name_short));
|
||||
RegCloseKey(service_hkey);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Look for alternate to default ServiceMain entry point */
|
||||
ret = RegQueryValueExA(service_hkey, service_main, NULL, NULL, NULL, ®_size);
|
||||
if (ret == ERROR_SUCCESS)
|
||||
{
|
||||
/* Add space for potentially missing NULL terminator, allocate, and
|
||||
* fill with the registry value */
|
||||
size = reg_size + 1;
|
||||
dll_service_main = HeapAlloc(GetProcessHeap(), 0, size);
|
||||
ret = RegQueryValueExA(service_hkey, service_main, NULL, NULL,
|
||||
(LPBYTE)dll_service_main, ®_size);
|
||||
if (ret != ERROR_SUCCESS)
|
||||
{
|
||||
RegCloseKey(service_hkey);
|
||||
goto cleanup;
|
||||
}
|
||||
dll_service_main[size - 1] = '\0';
|
||||
}
|
||||
RegCloseKey(service_hkey);
|
||||
|
||||
/* Load the DLL and obtain a pointer to ServiceMain entry point */
|
||||
library = LoadLibraryW(dll_name_long);
|
||||
if (!library)
|
||||
{
|
||||
WINE_ERR("failed to load library %s, err=%u\n",
|
||||
wine_dbgstr_w(dll_name_long), GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
if (dll_service_main)
|
||||
{
|
||||
service_main_func =
|
||||
(LPSERVICE_MAIN_FUNCTIONW) GetProcAddress(library, dll_service_main);
|
||||
}
|
||||
else
|
||||
{
|
||||
service_main_func =
|
||||
(LPSERVICE_MAIN_FUNCTIONW) GetProcAddress(library, service_main);
|
||||
}
|
||||
if (!service_main_func)
|
||||
{
|
||||
WINE_ERR("cannot locate ServiceMain procedure in DLL for %s\n",
|
||||
wine_dbgstr_w(service_name));
|
||||
FreeLibrary(library);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Fill in the service table entry */
|
||||
service_table_entry->lpServiceName = service_name;
|
||||
service_table_entry->lpServiceProc = service_main_func;
|
||||
success = TRUE;
|
||||
|
||||
cleanup:
|
||||
HeapFree(GetProcessHeap(), 0, service_param_key);
|
||||
HeapFree(GetProcessHeap(), 0, dll_name_short);
|
||||
HeapFree(GetProcessHeap(), 0, dll_name_long);
|
||||
HeapFree(GetProcessHeap(), 0, dll_service_main);
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Initialize the service table for a list (REG_MULTI_SZ) of services */
|
||||
static BOOL StartGroupServices(LPWSTR services)
|
||||
{
|
||||
LPWSTR service_name = NULL;
|
||||
SERVICE_TABLE_ENTRYW *service_table = NULL;
|
||||
DWORD service_count;
|
||||
|
||||
/* Count the services to load */
|
||||
service_count = 0;
|
||||
service_name = services;
|
||||
while (*service_name != '\0')
|
||||
{
|
||||
++service_count;
|
||||
service_name = service_name + lstrlenW(service_name);
|
||||
++service_name;
|
||||
}
|
||||
WINE_TRACE("Service group %s contains %d services\n",
|
||||
wine_dbgstr_w(services), service_count);
|
||||
|
||||
/* Populate the service table */
|
||||
service_table = HeapAlloc(GetProcessHeap(), 0,
|
||||
(service_count + 1) * sizeof(SERVICE_TABLE_ENTRYW));
|
||||
service_count = 0;
|
||||
service_name = services;
|
||||
while (*service_name != '\0')
|
||||
{
|
||||
if (!AddServiceElem(service_name, &service_table[service_count]))
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, service_table);
|
||||
return FALSE;
|
||||
}
|
||||
++service_count;
|
||||
service_name = service_name + lstrlenW(service_name);
|
||||
++service_name;
|
||||
}
|
||||
service_table[service_count].lpServiceName = NULL;
|
||||
service_table[service_count].lpServiceProc = NULL;
|
||||
|
||||
/* Start the services */
|
||||
if (!StartServiceCtrlDispatcherW(service_table))
|
||||
{
|
||||
WINE_ERR("StartServiceCtrlDispatcherW failed to start %s: %u\n",
|
||||
wine_dbgstr_w(services), GetLastError());
|
||||
HeapFree(GetProcessHeap(), 0, service_table);
|
||||
return FALSE;
|
||||
}
|
||||
HeapFree(GetProcessHeap(), 0, service_table);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Find the list of services associated with a group name and start those
|
||||
* services */
|
||||
static BOOL LoadGroup(PWCHAR group_name)
|
||||
{
|
||||
HKEY group_hkey = NULL;
|
||||
LPWSTR services = NULL;
|
||||
LONG ret;
|
||||
|
||||
WINE_TRACE("Loading service group for %s\n", wine_dbgstr_w(group_name));
|
||||
|
||||
/* Lookup group_name value of svchost registry entry */
|
||||
ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, svchost_path, 0,
|
||||
KEY_READ, &group_hkey);
|
||||
if (ret != ERROR_SUCCESS)
|
||||
{
|
||||
WINE_ERR("cannot open key %s, err=%d\n",
|
||||
wine_dbgstr_w(svchost_path), ret);
|
||||
return FALSE;
|
||||
}
|
||||
services = GetRegValue(group_hkey, group_name);
|
||||
RegCloseKey(group_hkey);
|
||||
if (!services)
|
||||
{
|
||||
WINE_ERR("cannot find registry value %s in %s\n",
|
||||
wine_dbgstr_w(group_name), wine_dbgstr_w(svchost_path));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Start services */
|
||||
if (StartGroupServices(services) == FALSE)
|
||||
{
|
||||
WINE_TRACE("Failed to start service group\n");
|
||||
HeapFree(GetProcessHeap(), 0, services);
|
||||
return FALSE;
|
||||
}
|
||||
HeapFree(GetProcessHeap(), 0, services);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Load svchost group specified on the command line via the /k option */
|
||||
int wmain(int argc, WCHAR *argv[])
|
||||
{
|
||||
int option_index;
|
||||
|
||||
WINE_TRACE("\n");
|
||||
|
||||
for (option_index = 1; option_index < argc; option_index++)
|
||||
{
|
||||
if (lstrcmpiW(argv[option_index], ks) == 0 ||
|
||||
lstrcmpiW(argv[option_index], kd) == 0)
|
||||
{
|
||||
++option_index;
|
||||
if (option_index >= argc)
|
||||
{
|
||||
WINE_ERR("Must specify group to initialize\n");
|
||||
return 0;
|
||||
}
|
||||
if (!LoadGroup(argv[option_index]))
|
||||
{
|
||||
WINE_ERR("Failed to load requested group: %s\n",
|
||||
wine_dbgstr_w(argv[option_index]));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WINE_FIXME("Unrecognized option: %s\n",
|
||||
wine_dbgstr_w(argv[option_index]));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue