From f76ff15ad14d568f5e2114ce8c3f1017713397f5 Mon Sep 17 00:00:00 2001 From: Mikolaj Zalewski Date: Thu, 20 Sep 2007 18:12:02 -0700 Subject: [PATCH] services.exe: load services list from registry --- programs/services/Makefile.in | 5 + programs/services/services.c | 174 +++++++++++++++++++++++++++++++++++++++++ programs/services/services.h | 24 ++++++ programs/services/utils.c | 149 +++++++++++++++++++++++++++++++++++ 4 files changed, 350 insertions(+), 2 deletions(-) diff --git a/programs/services/Makefile.in b/programs/services/Makefile.in index 11c17e9..0aa642c 100644 --- a/programs/services/Makefile.in +++ b/programs/services/Makefile.in @@ -4,9 +4,10 @@ SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = services.exe APPMODE = -mwindows -IMPORTS = kernel32 +IMPORTS = advapi32 kernel32 -C_SRCS = services.c +C_SRCS = services.c \ + utils.c @MAKE_PROG_RULES@ diff --git a/programs/services/services.c b/programs/services/services.c index 731c9b0..1bb6769 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -21,8 +21,182 @@ #define WIN32_LEAN_AND_MEAN #include +#include +#include + +#include "wine/list.h" +#include "wine/unicode.h" +#include "wine/debug.h" + +#include "services.h" + +#define MAX_SERVICE_NAME 260 + +WINE_DEFAULT_DEBUG_CHANNEL(services); + +struct list g_services; + +/* Registry constants */ +static const WCHAR SZ_SERVICES_KEY[] = { '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 }; + +/* Service key values names */ +static const WCHAR SZ_DISPLAY_NAME[] = {'D','i','s','p','l','a','y','N','a','m','e',0 }; +static const WCHAR SZ_TYPE[] = {'T','y','p','e',0 }; +static const WCHAR SZ_START[] = {'S','t','a','r','t',0 }; +static const WCHAR SZ_ERROR[] = {'E','r','r','o','r','C','o','n','t','r','o','l',0 }; +static const WCHAR SZ_IMAGE_PATH[] = {'I','m','a','g','e','P','a','t','h',0}; +static const WCHAR SZ_GROUP[] = {'G','r','o','u','p',0}; +static const WCHAR SZ_DEPEND_ON_SERVICE[] = {'D','e','p','e','n','d','O','n','S','e','r','v','i','c','e',0}; +static const WCHAR SZ_DEPEND_ON_GROUP[] = {'D','e','p','e','n','d','O','n','G','r','o','u','p',0}; +static const WCHAR SZ_OBJECT_NAME[] = {'O','b','j','e','c','t','N','a','m','e',0}; +static const WCHAR SZ_TAG[] = {'T','a','g',0}; +static const WCHAR SZ_DESCRIPTION[] = {'D','e','s','c','r','i','p','t','i','o','n',0}; + + +void free_service_entry(service_entry *entry) +{ + HeapFree(GetProcessHeap(), 0, entry->name); + HeapFree(GetProcessHeap(), 0, entry->config.lpBinaryPathName); + HeapFree(GetProcessHeap(), 0, entry->config.lpDependencies); + HeapFree(GetProcessHeap(), 0, entry->config.lpLoadOrderGroup); + HeapFree(GetProcessHeap(), 0, entry->config.lpServiceStartName); + HeapFree(GetProcessHeap(), 0, entry->config.lpDisplayName); + HeapFree(GetProcessHeap(), 0, entry->description); + HeapFree(GetProcessHeap(), 0, entry); +} + +DWORD load_service_config(HKEY hKey, service_entry *entry) +{ + DWORD err; + WCHAR *wptr; + + if ((err = load_reg_string(hKey, SZ_IMAGE_PATH, TRUE, &entry->config.lpBinaryPathName)) != 0) + return err; + if ((err = load_reg_string(hKey, SZ_GROUP, 0, &entry->config.lpLoadOrderGroup)) != 0) + return err; + if ((err = load_reg_string(hKey, SZ_OBJECT_NAME, TRUE, &entry->config.lpServiceStartName)) != 0) + return err; + if ((err = load_reg_string(hKey, SZ_DISPLAY_NAME, 0, &entry->config.lpDisplayName)) != 0) + return err; + if ((err = load_reg_string(hKey, SZ_DESCRIPTION, 0, &entry->description)) != 0) + return err; + if ((err = load_reg_multisz(hKey, SZ_DEPEND_ON_SERVICE, &entry->dependOnServices)) != 0) + return err; + if ((err = load_reg_multisz(hKey, SZ_DEPEND_ON_GROUP, &entry->dependOnGroups)) != 0) + return err; + + if ((err = load_reg_dword(hKey, SZ_TYPE, &entry->config.dwServiceType)) != 0) + return err; + if ((err = load_reg_dword(hKey, SZ_START, &entry->config.dwStartType)) != 0) + return err; + if ((err = load_reg_dword(hKey, SZ_ERROR, &entry->config.dwErrorControl)) != 0) + return err; + if ((err = load_reg_dword(hKey, SZ_TAG, &entry->config.dwTagId)) != 0) + return err; + + WINE_TRACE("Image path = %s\n", wine_dbgstr_w(entry->config.lpBinaryPathName) ); + WINE_TRACE("Group = %s\n", wine_dbgstr_w(entry->config.lpLoadOrderGroup) ); + WINE_TRACE("Service account name = %s\n", wine_dbgstr_w(entry->config.lpServiceStartName) ); + WINE_TRACE("Display name = %s\n", wine_dbgstr_w(entry->config.lpDisplayName) ); + WINE_TRACE("Service dependancies : %s\n", entry->dependOnServices[0] ? "" : "(none)"); + for (wptr = entry->dependOnServices; *wptr; wptr += strlenW(wptr) + 1) + WINE_TRACE(" * %s\n", wine_dbgstr_w(wptr)); + WINE_TRACE("Group dependancies : %s\n", entry->dependOnGroups[0] ? "" : "(none)"); + for (wptr = entry->dependOnGroups; *wptr; wptr += strlenW(wptr) + 1) + WINE_TRACE(" * %s\n", wine_dbgstr_w(wptr)); + + return ERROR_SUCCESS; +} + +BOOL validate_service_config(service_entry *entry) +{ + if (entry->config.dwServiceType & SERVICE_WIN32 && (entry->config.lpBinaryPathName == NULL || !entry->config.lpBinaryPathName[0])) + { + WINE_ERR("Service %s is Win32 but have no image path set\n", wine_dbgstr_w(entry->name)); + return FALSE; + } + return TRUE; +} + +DWORD load_services() +{ + HKEY hServicesRootKey; + DWORD err; + int i; + + if ((err = RegOpenKeyW(HKEY_LOCAL_MACHINE, SZ_SERVICES_KEY, &hServicesRootKey)) != 0) + { + WINE_ERR("Couldn't open services key\n"); + return err; + } + + for (i = 0; TRUE; i++) + { + WCHAR szName[MAX_SERVICE_NAME]; + service_entry *entry; + HKEY hServiceKey; + + err = RegEnumKeyW(hServicesRootKey, i, szName, MAX_SERVICE_NAME); + if (err == ERROR_NO_MORE_ITEMS) + break; + + if (err != 0) + { + WINE_ERR("Error %d reading key %d name - skipping\n", err, i); + continue; + } + + entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entry)); + entry->name = strdupW(szName); + + WINE_TRACE("Loading service %s\n", wine_dbgstr_w(szName)); + err = RegOpenKeyW(hServicesRootKey, szName, &hServiceKey); + if (err == 0) + { + err = load_service_config(hServiceKey, entry); + RegCloseKey(hServiceKey); + } + + if (err != 0) + { + WINE_ERR("Error %d reading registry key for service %s - skipping\n", err, wine_dbgstr_w(szName)); + free_service_entry(entry); + continue; + } + + if (entry->config.dwServiceType == 0) + { + /* Maybe an application only wrote some configuration in the service key. Continue silently */ + WINE_TRACE("Even the service type not set for service %s - skipping\n", wine_dbgstr_w(szName)); + free_service_entry(entry); + continue; + } + + if (!validate_service_config(entry)) + { + WINE_ERR("Invalid configuration of service %s - skipping\n", wine_dbgstr_w(szName)); + free_service_entry(entry); + continue; + } + + entry->status.dwServiceType = entry->config.dwServiceType; + entry->status.dwCurrentState = SERVICE_STOPPED; + entry->status.dwWin32ExitCode = ERROR_SERVICE_NEVER_STARTED; + /* all other fields are zero */ + + list_add_tail(&g_services, &entry->entry); + } + RegCloseKey(hServicesRootKey); + return 0; +} int main(int argc, char *argv[]) { + DWORD err; + list_init(&g_services); + if ((err = load_services()) != 0) + return err; return 0; } diff --git a/programs/services/services.h b/programs/services/services.h new file mode 100644 index 0000000..f347c5b --- /dev/null +++ b/programs/services/services.h @@ -0,0 +1,24 @@ +#ifndef WINE_PROGRAMS_UTILS_H_ +#define WINE_PROGRAMS_UTILS_H_ + +#include "wine/list.h" + +typedef struct _service_entry +{ + struct list entry; + LPWSTR name; + SERVICE_STATUS_PROCESS status; + QUERY_SERVICE_CONFIGW config; + LPWSTR description; + LPWSTR dependOnServices; + LPWSTR dependOnGroups; +} service_entry; + +/* from utils.c */ +LPWSTR strdupW(LPWSTR str); + +DWORD load_reg_string(HKEY hKey, LPCWSTR szValue, BOOL bExpand, LPWSTR *output); +DWORD load_reg_multisz(HKEY hKey, LPCWSTR szValue, LPWSTR *output); +DWORD load_reg_dword(HKEY hKey, LPCWSTR szValue, DWORD *output); + +#endif /*WINE_PROGRAMS_UTILS_H_*/ diff --git a/programs/services/utils.c b/programs/services/utils.c new file mode 100644 index 0000000..35d21bb --- /dev/null +++ b/programs/services/utils.c @@ -0,0 +1,149 @@ +/* + * Services.exe - some utility functions + * + * Copyright 2007 Google (Mikolaj Zalewski) + * + * 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 + */ + +#define WIN32_LEAN_AND_MEAN + +#include +#include + +#include "wine/unicode.h" +#include "wine/debug.h" +#include "services.h" + +WINE_DEFAULT_DEBUG_CHANNEL(services); + +LPWSTR strdupW(LPWSTR str) +{ + int len = strlenW(str); + WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(len+1)); + if (buf == NULL) + return NULL; + strcpyW(buf, str); + return buf; +} + +DWORD load_reg_string(HKEY hKey, LPCWSTR szValue, BOOL bExpand, LPWSTR *output) +{ + DWORD size, type; + LPWSTR buf = NULL; + DWORD err; + + *output = NULL; + if ((err = RegQueryValueExW(hKey, szValue, 0, &type, NULL, &size)) != 0) + { + if (err == ERROR_FILE_NOT_FOUND) + return ERROR_SUCCESS; + goto failed; + } + if (!(type == REG_SZ || (type == REG_EXPAND_SZ && bExpand))) + { + err = ERROR_INVALID_DATATYPE; + goto failed; + } + buf = HeapAlloc(GetProcessHeap(), 0, size + 2); + if ((err = RegQueryValueExW(hKey, szValue, 0, &type, (LPBYTE)buf, &size)) != 0) + goto failed; + buf[size/2] = 0; + + if (type == REG_EXPAND_SZ) + { + LPWSTR str; + DWORD size = ExpandEnvironmentStringsW(buf, NULL, 0); + if (size == 0) + { + err = GetLastError(); + goto failed; + } + str = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); + ExpandEnvironmentStringsW(buf, str, size); + HeapFree(GetProcessHeap(), 0, buf); + *output = str; + } + else + *output = buf; + return ERROR_SUCCESS; + +failed: + WINE_ERR("Error %d while reading value %s\n", err, wine_dbgstr_w(szValue)); + if (buf) + HeapFree(GetProcessHeap(), 0, buf); + return err; +} + +DWORD load_reg_multisz(HKEY hKey, LPCWSTR szValue, LPWSTR *output) +{ + DWORD size, type; + LPWSTR buf = NULL; + DWORD err; + + *output = NULL; + if ((err = RegQueryValueExW(hKey, szValue, 0, &type, NULL, &size)) != 0) + { + if (err == ERROR_FILE_NOT_FOUND) + { + *output = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR)); + return ERROR_SUCCESS; + } + goto failed; + } + if (type != REG_MULTI_SZ) + { + err = ERROR_INVALID_DATATYPE; + goto failed; + } + buf = HeapAlloc(GetProcessHeap(), 0, size + 4); + if ((err = RegQueryValueExW(hKey, szValue, 0, &type, (LPBYTE)buf, &size)) != 0) + goto failed; + buf[size/2] = 0; + buf[size/2 + 1] = 0; + *output = buf; + return ERROR_SUCCESS; + +failed: + WINE_ERR("Error %d while reading value %s\n", err, wine_dbgstr_w(szValue)); + if (buf) + HeapFree(GetProcessHeap(), 0, buf); + return err; +} + +DWORD load_reg_dword(HKEY hKey, LPCWSTR szValue, DWORD *output) +{ + DWORD size, type; + DWORD err; + + *output = 0; + size = sizeof(DWORD); + if ((err = RegQueryValueExW(hKey, szValue, 0, &type, (LPBYTE)output, &size)) != 0) + { + if (err == ERROR_FILE_NOT_FOUND) + return ERROR_SUCCESS; + goto failed; + } + if (type != REG_DWORD || size != sizeof(DWORD)) + { + err = ERROR_INVALID_DATATYPE; + goto failed; + } + return ERROR_SUCCESS; + +failed: + WINE_ERR("Error %d while reading value %s\n", err, wine_dbgstr_w(szValue)); + return err; +} -- 1.4.1