svchost: Added base implementation of svchost (1/26)

Roy Shea roy at cs.hmc.edu
Mon Nov 19 23:34:16 CST 2007


Resubmission of svchost.  This resubmission returns to a unicode
version of svchost.  Relative to the svchost patch sent on Nov 16:

http://www.winehq.org/pipermail/wine-patches/2007-November/046858.html

it fixes the "off by two" error in UnicodeToAscii, condenses the
HeapFrees in AddServiceElem, and removes an unused linked library.

This resubmission has been tested against the other patches in the set
and does not effect any of the other patches in the set.

Change log:
    Added base implementation of svchost

-------------- next part --------------
Added base implementation of svchost
---
 programs/svchost/Makefile.in |   15 ++
 programs/svchost/svchost.c   |  414 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 429 insertions(+), 0 deletions(-)
 create mode 100644 programs/svchost/Makefile.in
 create mode 100644 programs/svchost/svchost.c

diff --git a/programs/svchost/Makefile.in b/programs/svchost/Makefile.in
new file mode 100644
index 0000000..a62f1bd
--- /dev/null
+++ b/programs/svchost/Makefile.in
@@ -0,0 +1,15 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = svchost.exe
+APPMODE   = -municode
+IMPORTS   = advapi32 kernel32
+
+C_SRCS = \
+	svchost.c
+
+ at MAKE_PROG_RULES@
+
+ at DEPENDENCIES@  # everything below this line is overwritten by make depend
+
diff --git a/programs/svchost/svchost.c b/programs/svchost/svchost.c
new file mode 100644
index 0000000..6c3c738
--- /dev/null
+++ b/programs/svchost/svchost.c
@@ -0,0 +1,414 @@
+/*
+ * 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_seperator[] = {'\\',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 WCHAR service_main[] = {
+    'S','e','r','v','i','c','e','M','a','i','n',0};
+
+/* Allocate and initializes an ASCII version of the Unicode string */
+static LPSTR UnicodeToAscii(const WCHAR *unicode)
+{
+    int size;
+    LPSTR ascii;
+    int i;
+
+    size = lstrlenW(unicode) + 1;
+    ascii= HeapAlloc(GetProcessHeap(), 0, size);
+    for (i=0; i<size; i++)
+    {
+        if (unicode[i] > 127)
+        {
+            WINE_ERR("Unable to convert unicode string \"%s\" to ascii\n",
+                    wine_dbgstr_w(unicode));
+            HeapFree(GetProcessHeap(), 0, ascii);
+            return NULL;
+        }
+        ascii[i] = (char) unicode[i];
+    }
+
+    return ascii;
+}
+
+/* Allocate and initialize a WSTR containing the queried value */
+static LPWSTR GetRegValue(HKEY service_key, const WCHAR *value_name)
+{
+    DWORD type;
+    DWORD size;
+    LONG ret;
+    LPWSTR value;
+    LPWSTR tmp_value;
+
+    WINE_TRACE("\n");
+
+    ret = RegQueryValueExW(service_key, value_name, NULL, &type,
+            NULL, &size);
+    if (ret != ERROR_SUCCESS)
+    {
+        return NULL;
+    }
+    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;
+    }
+
+    /* Verify proper NULL termination for string types */
+    switch (type)
+    {
+        case REG_SZ: /* Fall through */
+        case REG_EXPAND_SZ:
+            if (value[size-1] != '\0')
+            {
+                WINE_TRACE("NULL terminating registry entry: %s\n",
+                        wine_dbgstr_w(value));
+                tmp_value = HeapReAlloc(GetProcessHeap(),
+                        HEAP_ZERO_MEMORY, value, size+1);
+                if (!tmp_value)
+                {
+                    HeapFree(GetProcessHeap(), 0, value);
+                    return NULL;
+                }
+                value = tmp_value;
+            }
+            break;
+        case REG_MULTI_SZ:
+            if (value[size-1] != '\0' || value[size-2] != '\0')
+            {
+                WINE_TRACE("NULL terminating registry entry: %s\n",
+                        wine_dbgstr_w(value));
+                tmp_value = HeapReAlloc(GetProcessHeap(),
+                        HEAP_ZERO_MEMORY, value, size+2);
+                if (!tmp_value)
+                {
+                    HeapFree(GetProcessHeap(), 0, value);
+                    return NULL;
+                }
+                value = tmp_value;
+            }
+            break;
+        default:
+            /* No need to check other types */
+            break;
+    }
+    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)
+{
+    HKEY service_hkey = NULL;
+    LONG ret;
+    LPWSTR service_param_key = NULL;
+    LPWSTR dll_name_short = NULL;
+    LPWSTR dll_name_long = NULL;
+    LPWSTR dll_service_main = NULL;
+    LPSTR service_main_a = NULL;
+    HMODULE library = NULL;
+    LPSERVICE_MAIN_FUNCTIONW service_main_func = NULL;
+    BOOL success = FALSE;
+
+    WINE_TRACE("Adding element for %s\n", wine_dbgstr_w(service_name));
+
+    /* Construct registry path to the service's parameters key */
+    service_param_key = HeapAlloc( GetProcessHeap(), 0,
+            (lstrlenW(service_reg_path) +
+             lstrlenW(reg_seperator) +
+             lstrlenW(service_name) +
+             lstrlenW(reg_seperator) +
+             lstrlenW(parameters) +
+             1) * sizeof(WCHAR));
+    lstrcpyW(service_param_key, service_reg_path);
+    lstrcatW(service_param_key, reg_seperator);
+    lstrcatW(service_param_key, service_name);
+    lstrcatW(service_param_key, reg_seperator);
+    lstrcatW(service_param_key, parameters);
+    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 */
+    dll_service_main = GetRegValue(service_hkey, service_main);
+    RegCloseKey(service_hkey);
+    if (!dll_service_main)
+    {
+        service_main_a = UnicodeToAscii(service_main);
+    }
+    else
+    {
+        service_main_a = UnicodeToAscii(dll_service_main);
+    }
+    if (!service_main_a)
+    {
+        goto cleanup;
+    }
+
+    /* 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;
+    }
+    service_main_func =
+        (LPSERVICE_MAIN_FUNCTIONW) GetProcAddress(library, service_main_a);
+    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);
+    HeapFree(GetProcessHeap(), 0, service_main_a);
+    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_name = services;
+    service_count = 0;
+    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;
+}
+
+
+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;
+}
+
-- 
1.5.3.4



More information about the wine-patches mailing list