[PATCH] services.exe/advapi32: move CreateService, OpenService and DeleteService to services.exe

Mikołaj Zalewski mikolaj at zalewski.pl
Sat Mar 15 10:42:38 CDT 2008


---
 dlls/advapi32/service.c      |  237 +++++++++++-------------------------------
 include/wine/svcctl.idl      |   35 ++++++
 programs/services/rpc.c      |  196 ++++++++++++++++++++++++++++++++++
 programs/services/services.c |  194 ++++++++++++++++++++++++++++++++++-
 programs/services/services.h |   25 +++++
 programs/services/utils.c    |   20 +++-
 6 files changed, 529 insertions(+), 178 deletions(-)

diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c
index 8ac5bbd..52a3f1c 100644
--- a/dlls/advapi32/service.c
+++ b/dlls/advapi32/service.c
@@ -1208,17 +1208,15 @@ CloseServiceHandle( SC_HANDLE hSCObject )
     }
 
     obj = (struct sc_handle *)hSCObject;
-    if (obj->rpc_handle)   /* service handles currently don't have RPC connections */
-        err = svcctl_CloseServiceHandle(obj->rpc_handle, &obj->server_handle);
-    else
-        err = ERROR_SUCCESS;
-    sc_handle_free( obj );
+    err = svcctl_CloseServiceHandle(obj->rpc_handle, &obj->server_handle);
 
     if (err != ERROR_SUCCESS)
     {
         SetLastError(err);
         return FALSE;
     }
+
+    sc_handle_free( obj );
     return TRUE;
 }
 
@@ -1262,8 +1260,7 @@ SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
 {
     struct sc_manager *hscm;
     struct sc_service *hsvc;
-    HKEY hKey;
-    long r;
+    DWORD err;
     DWORD len;
     DWORD new_mask = dwDesiredAccess;
 
@@ -1281,13 +1278,6 @@ SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
         SetLastError(ERROR_INVALID_ADDRESS);
         return NULL;
     }
-
-    r = RegOpenKeyExW( hscm->hkey, lpServiceName, 0, KEY_ALL_ACCESS, &hKey );
-    if (r!=ERROR_SUCCESS)
-    {
-        SetLastError( ERROR_SERVICE_DOES_NOT_EXIST );
-        return NULL;
-    }
     
     len = strlenW(lpServiceName)+1;
     hsvc = sc_handle_alloc( SC_HTYPE_SERVICE,
@@ -1295,18 +1285,32 @@ SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
                             sc_handle_destroy_service );
     if (!hsvc)
     {
-        RegCloseKey(hKey);
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
         return NULL;
     }
     strcpyW( hsvc->name, lpServiceName );
-    hsvc->hkey = hKey;
-
-    RtlMapGenericMask(&new_mask, &svc_generic);
-    hsvc->dwAccess = new_mask;
 
     /* add reference to SCM handle */
     hscm->hdr.ref_count++;
     hsvc->scm = hscm;
+    hsvc->hdr.rpc_handle = hscm->hdr.rpc_handle;
+    
+    err = svcctl_OpenServiceW(hscm->hdr.rpc_handle, &hscm->hdr.server_handle, lpServiceName, dwDesiredAccess, &hsvc->hdr.server_handle);
+    
+    if (err != ERROR_SUCCESS)
+    {
+        sc_handle_free(&hsvc->hdr);
+        SetLastError(err);
+        return NULL;
+    }
+
+    /* for parts of advapi32 not using services.exe yet */
+    RtlMapGenericMask(&new_mask, &svc_generic);
+    hsvc->dwAccess = new_mask;
+
+    err = RegOpenKeyExW( hscm->hkey, lpServiceName, 0, KEY_ALL_ACCESS, &hsvc->hkey );
+    if (err != ERROR_SUCCESS)
+        ERR("Shouldn't hapen - service key for service validated by services.exe doesn't exist\n");
 
     TRACE("returning %p\n",hsvc);
 
@@ -1327,15 +1331,9 @@ CreateServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
 {
     struct sc_manager *hscm;
     struct sc_service *hsvc = NULL;
-    HKEY hKey;
-    LONG r;
-    DWORD dp, len;
-    struct reg_value val[10];
-    int n = 0;
     DWORD new_mask = dwDesiredAccess;
-    DWORD index = 0;
-    WCHAR buffer[MAX_PATH];
-    BOOL displayname_exists = FALSE;
+    DWORD len, err;
+    SIZE_T depslen = 0, passwdlen;
 
     TRACE("%p %s %s\n", hSCManager, 
           debugstr_w(lpServiceName), debugstr_w(lpDisplayName));
@@ -1353,164 +1351,56 @@ CreateServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
         return NULL;
     }
 
-    if (!(hscm->dwAccess & SC_MANAGER_CREATE_SERVICE))
+    if (lpDependencies)
     {
-        SetLastError(ERROR_ACCESS_DENIED);
-        return NULL;
-    }
-
-    if (!lpServiceName[0])
-    {
-        SetLastError(ERROR_INVALID_NAME);
-        return NULL;
+        const WCHAR *wptr = lpDependencies;
+        while (*wptr)
+            wptr += strlenW(wptr)+1;
+        depslen = (wptr - lpDependencies + 1)*sizeof(WCHAR);
     }
+    else
+        depslen = 0;
 
-    if (!lpBinaryPathName[0])
-    {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return NULL;
-    }
-
-    /* ServiceType can only be one value (except for SERVICE_INTERACTIVE_PROCESS which can be used
-     * together with SERVICE_WIN32_OWN_PROCESS or SERVICE_WIN32_SHARE_PROCESS when the service
-     * runs under the LocalSystem account)
-     */
-    switch (dwServiceType)
-    {
-    case SERVICE_KERNEL_DRIVER:
-    case SERVICE_FILE_SYSTEM_DRIVER:
-    case SERVICE_WIN32_OWN_PROCESS:
-    case SERVICE_WIN32_SHARE_PROCESS:
-        /* No problem */
-        break;
-    case SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS:
-    case SERVICE_WIN32_SHARE_PROCESS | SERVICE_INTERACTIVE_PROCESS:
-        /* FIXME : Do we need a more thorough check? */
-        if (lpServiceStartName)
-        {
-            SetLastError(ERROR_INVALID_PARAMETER);
-            return NULL;
-        }
-        break;
-    default:
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return NULL;
-    }
-
-    if (!lpServiceStartName && (dwServiceType & SERVICE_WIN32))
-            lpServiceStartName = szLocalSystem;
-
-    /* StartType can only be a single value (if several values are mixed the result is probably not what was intended) */
-    if (dwStartType > SERVICE_DISABLED)
-    {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return NULL;
-    }
+    if (lpPassword)
+        passwdlen = (strlenW(lpPassword) + 1) * sizeof(WCHAR);
+    else
+        passwdlen = 0;
 
-    /* SERVICE_BOOT_START and SERVICE_SYSTEM_START or only allowed for driver services */
-    if (((dwStartType == SERVICE_BOOT_START) || (dwStartType == SERVICE_SYSTEM_START)) &&
-        ((dwServiceType & SERVICE_WIN32_OWN_PROCESS) || (dwServiceType & SERVICE_WIN32_SHARE_PROCESS)))
+    len = strlenW(lpServiceName)+1;
+    len = sizeof (struct sc_service) + len*sizeof(WCHAR);
+    hsvc = sc_handle_alloc( SC_HTYPE_SERVICE, len, sc_handle_destroy_service );
+    if( !hsvc )
     {
-        SetLastError(ERROR_INVALID_PARAMETER);
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
         return NULL;
     }
+    lstrcpyW( hsvc->name, lpServiceName );
 
-    /* Loop through the registry to check if the service already exists and to
-     * check if we can use the given displayname.
-     * FIXME: Should we use EnumServicesStatusEx?
-     */
-    len = sizeof(buffer);
-    while (RegEnumKeyExW(hscm->hkey, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
-    {
-        HKEY service_key;
-
-        /* Open service first before deciding whether it already exists or not
-         * It could be that it's not a valid service, but only the registry key itself exists
-         */
-        if (RegOpenKeyExW(hscm->hkey, buffer, 0, KEY_READ, &service_key) == ERROR_SUCCESS)
-        {
-            WCHAR name[MAX_PATH];
-            DWORD size = sizeof(name);
-
-            if (RegQueryValueExW(service_key, szDisplayName, NULL, NULL, (LPBYTE)name, &size) == ERROR_SUCCESS)
-            {
-                if (lpDisplayName && (!lstrcmpiW(lpDisplayName, name)
-                                   || !lstrcmpiW(lpDisplayName, buffer)))
-                    displayname_exists = TRUE;
-
-                if (!lstrcmpiW(lpServiceName, buffer))
-                {
-                    RegCloseKey(service_key);
-                    SetLastError(ERROR_SERVICE_EXISTS);
-                    return NULL;
-                }
-            }
-            RegCloseKey(service_key);
-        }
-        index++;
-        len = sizeof(buffer);
-    }
+    hsvc->hdr.rpc_handle = hscm->hdr.rpc_handle;
+    hsvc->scm = hscm;
+    hscm->hdr.ref_count++;
 
-    if (displayname_exists)
-    {
-        SetLastError(ERROR_DUPLICATE_SERVICE_NAME);
-        return NULL;
-    }
+    err = svcctl_CreateServiceW(hscm->hdr.rpc_handle, &hscm->hdr.server_handle, lpServiceName,
+            lpDisplayName, dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
+            lpBinaryPathName, lpLoadOrderGroup, lpdwTagId, (LPBYTE)lpDependencies, depslen,
+            lpServiceStartName, (LPBYTE)lpPassword, passwdlen, &hsvc->hdr.server_handle);
 
-    r = RegCreateKeyExW(hscm->hkey, lpServiceName, 0, NULL,
-                       REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dp);
-    if (r!=ERROR_SUCCESS)
+    if (err != ERROR_SUCCESS)
     {
-        /* FIXME: Should we set an error? */
+        SetLastError(err);
+        sc_handle_free(&hsvc->hdr);
         return NULL;
     }
 
-    if( lpDisplayName )
-        service_set_string( &val[n++], szDisplayName, lpDisplayName );
-
-    service_set_dword( &val[n++], szType, &dwServiceType );
-    service_set_dword( &val[n++], szStart, &dwStartType );
-    service_set_dword( &val[n++], szError, &dwErrorControl );
-
-    service_set_string( &val[n++], szImagePath, lpBinaryPathName );
-
-    if( lpLoadOrderGroup )
-        service_set_string( &val[n++], szGroup, lpLoadOrderGroup );
-
-    /* FIXME: lpDependencies is used to create both DependOnService and DependOnGroup
-     * There is no such key as what szDependencies refers to */
-    if( lpDependencies )
-        service_set_multi_string( &val[n++], szDependencies, lpDependencies );
-
-    if( lpPassword )
-        FIXME("Don't know how to add a Password for a service.\n");
-
-    if( lpServiceStartName )
-        service_set_string( &val[n++], szObjectName, lpServiceStartName );
-
-    r = service_write_values( hKey, val, n );
-    if( r != ERROR_SUCCESS )
-        goto error;
-
-    len = strlenW(lpServiceName)+1;
-    len = sizeof (struct sc_service) + len*sizeof(WCHAR);
-    hsvc = sc_handle_alloc( SC_HTYPE_SERVICE, len, sc_handle_destroy_service );
-    if( !hsvc )
-        goto error;
-    lstrcpyW( hsvc->name, lpServiceName );
-    hsvc->hkey = hKey;
+    /* for parts of advapi32 not using services.exe yet */
+    err = RegOpenKeyW(hscm->hkey, lpServiceName, &hsvc->hkey);
+    if (err != ERROR_SUCCESS)
+        WINE_ERR("Couldn't open key that should have been created by services.exe\n");
 
     RtlMapGenericMask(&new_mask, &svc_generic);
     hsvc->dwAccess = new_mask;
 
-    hsvc->scm = hscm;
-    hscm->hdr.ref_count++;
-
     return (SC_HANDLE) &hsvc->hdr;
-    
-error:
-    RegCloseKey( hKey );
-    return NULL;
 }
 
 
@@ -1573,6 +1463,7 @@ CreateServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName,
 BOOL WINAPI DeleteService( SC_HANDLE hService )
 {
     struct sc_service *hsvc;
+    DWORD err;
 
     hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE);
     if (!hsvc)
@@ -1580,21 +1471,17 @@ BOOL WINAPI DeleteService( SC_HANDLE hService )
         SetLastError( ERROR_INVALID_HANDLE );
         return FALSE;
     }
-
-    if (!(hsvc->dwAccess & DELETE))
+    
+    err = svcctl_DeleteService(hsvc->hdr.rpc_handle, &hsvc->hdr.server_handle);
+    if (err != 0)
     {
-        SetLastError(ERROR_ACCESS_DENIED);
+        SetLastError(err);
         return FALSE;
     }
-
+    
     /* Close the key to the service */
     RegCloseKey(hsvc->hkey);
-
-    /* Delete the service under the Service Control Manager key */
-    RegDeleteTreeW(hsvc->scm->hkey, hsvc->name);
-
     hsvc->hkey = NULL;
-
     return TRUE;
 }
 
diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl
index fefd12f..f25d4a3 100644
--- a/include/wine/svcctl.idl
+++ b/include/wine/svcctl.idl
@@ -56,6 +56,33 @@ cpp_quote("#define SVCCTL_STARTED_EVENT (const WCHAR[]){'_','_','w','i','n','e',
         [in,out] POLICY_HANDLE *handle
     );
 
+    /* Compatible with Windows function 0x02 */
+    DWORD svcctl_DeleteService(
+        [in] SvcCtlRpcHandle rpc_handle,
+        [in,ref] POLICY_HANDLE *hService
+    );
+
+    /* Compatible with Windows function 0x0c */
+    DWORD svcctl_CreateServiceW(
+        SvcCtlRpcHandle rpc_handle,
+        [in] POLICY_HANDLE *hSCManager,
+        [in] LPCWSTR lpServiceName,
+        [in,unique] LPCWSTR lpDisplayName,
+        [in] DWORD dwDesiredAccess,
+        [in] DWORD dwServiceType,
+        [in] DWORD dwStartType,
+        [in] DWORD dwErrorControl,
+        [in] LPCWSTR lpBinaryPathName,
+        [in,unique] LPCWSTR lpLoadOrderGroup,
+        [in,out,unique] DWORD *lpdwTagId,
+        [in,unique,size_is(dwDependenciesSize)] const BYTE *lpDependencies,
+        [in] DWORD dwDependenciesSize,
+        [in,unique] LPCWSTR lpServiceStartName,
+        [in,unique,size_is(dwPasswordSize)] const BYTE *lpPassword,
+        [in] DWORD dwPasswordSize,
+        [out] POLICY_HANDLE *phService
+    );
+
     /* Compatible with Windows function 0x0f */
     DWORD svcctl_OpenSCManagerW(
         SvcCtlRpcHandle rpc_handle,
@@ -65,4 +92,12 @@ cpp_quote("#define SVCCTL_STARTED_EVENT (const WCHAR[]){'_','_','w','i','n','e',
         [out] POLICY_HANDLE *handle
     );
 
+    /* Compatible with Windows function 0x10 */
+    DWORD svcctl_OpenServiceW(
+        SvcCtlRpcHandle rpc_handle,
+        [in] POLICY_HANDLE *hSCManager,
+        [in] LPCWSTR lpServiceName,
+        [in] DWORD dwDesiredAccess,
+        [out] POLICY_HANDLE *phService
+    );
 }
diff --git a/programs/services/rpc.c b/programs/services/rpc.c
index 123db66..af5c346 100644
--- a/programs/services/rpc.c
+++ b/programs/services/rpc.c
@@ -55,6 +55,13 @@ static const GENERIC_MAPPING g_scm_generic = {
     SC_MANAGER_ALL_ACCESS
 };
 
+static const GENERIC_MAPPING g_svc_generic = {
+    (STANDARD_RIGHTS_READ | SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS),
+    (STANDARD_RIGHTS_WRITE | SERVICE_CHANGE_CONFIG),
+    (STANDARD_RIGHTS_EXECUTE | SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_USER_DEFINED_CONTROL),
+    SERVICE_ALL_ACCESS
+};
+
 typedef struct _handle_uuid   /* we interpret the uuid field of the POLICY_HANDLE as such a structure */
 {
     ULONG index;
@@ -74,6 +81,7 @@ typedef struct _handle_data
     DWORD dwType;
     DWORD dwAccess;
     LONGLONG dwHandleKey;
+    service_entry *service;    /* valid only for service handles */
 } handle_data;
 
 static void init_policy_handle(POLICY_HANDLE *handle, ULONG index, handle_data *data)
@@ -137,6 +145,29 @@ static DWORD validate_policy_handle(POLICY_HANDLE *handle, DWORD dwType, DWORD d
     return ERROR_SUCCESS;
 }
 
+static DWORD validate_scm_handle(POLICY_HANDLE *handle, DWORD dwNeededAccess)
+{
+    handle_data *data;
+    DWORD err;
+
+    if ((err = validate_policy_handle(handle, HANDLE_TYPE_MANAGER, dwNeededAccess, &data)) != 0)
+        return err;
+    LeaveCriticalSection(&g_handle_table_cs);
+    return ERROR_SUCCESS;
+}
+
+static DWORD validate_service_handle(POLICY_HANDLE *handle, DWORD dwNeededAccess, service_entry **service)
+{
+    handle_data *data;
+    DWORD err;
+
+    if ((err = validate_policy_handle(handle, HANDLE_TYPE_SERVICE, dwNeededAccess, &data)) != 0)
+        return err;
+    *service = data->service;
+    LeaveCriticalSection(&g_handle_table_cs);
+    return ERROR_SUCCESS;
+}
+
 DWORD svcctl_OpenSCManagerW(
     SvcCtlRpcHandle rpc_handle,
     LPCWSTR MachineName,      /* Note: this parameter is ignored */
@@ -170,6 +201,167 @@ DWORD svcctl_OpenSCManagerW(
     return ERROR_SUCCESS;
 }
 
+static DWORD create_handle_for_service(service_entry *entry, DWORD dwDesiredAccess, POLICY_HANDLE *phService)
+{
+    handle_data *service_data;
+    ULONG index;
+
+    if (!(service_data = (handle_data *)RtlAllocateHandle(&g_handle_table, &index)))
+    {
+        release_service(entry);
+        return ERROR_NOT_ENOUGH_SERVER_MEMORY;
+    }
+    service_data->ulHandleFlags |= 1;  /* mark handle as used */
+
+    service_data->dwType = HANDLE_TYPE_SERVICE;
+    service_data->service = entry;
+    if (dwDesiredAccess & MAXIMUM_ALLOWED)
+        dwDesiredAccess |= SERVICE_ALL_ACCESS;
+    service_data->dwAccess = dwDesiredAccess;
+    RtlMapGenericMask(&service_data->dwAccess, &g_svc_generic);
+
+    init_policy_handle(phService, index, service_data);
+    return ERROR_SUCCESS;
+}
+
+DWORD svcctl_OpenServiceW(
+    SvcCtlRpcHandle rpc_handle,
+    POLICY_HANDLE *hSCManager,
+    LPCWSTR lpServiceName,
+    DWORD dwDesiredAccess,
+    POLICY_HANDLE *phService)
+{
+    service_entry *entry;
+    DWORD err;
+
+    WINE_TRACE("conn=%p (%s, %s, %x)\n", rpc_handle, wine_dbgstr_guid(&hSCManager->uuid), wine_dbgstr_w(lpServiceName), dwDesiredAccess);
+    if ((err = validate_scm_handle(hSCManager, 0)) != 0)
+        return err;
+    if (!validate_service_name(lpServiceName))
+        return ERROR_INVALID_NAME;
+
+    lock_services();
+    entry = find_service(lpServiceName);
+    if (entry != NULL)
+        entry->ref_count++;
+    unlock_services();
+
+    if (entry == NULL)
+        return ERROR_SERVICE_DOES_NOT_EXIST;
+
+    return create_handle_for_service(entry, dwDesiredAccess, phService);    
+}
+
+DWORD svcctl_CreateServiceW(
+    SvcCtlRpcHandle rpc_handle,
+    POLICY_HANDLE *hSCManager,
+    LPCWSTR lpServiceName,
+    LPCWSTR lpDisplayName,
+    DWORD dwDesiredAccess,
+    DWORD dwServiceType,
+    DWORD dwStartType,
+    DWORD dwErrorControl,
+    LPCWSTR lpBinaryPathName,
+    LPCWSTR lpLoadOrderGroup,
+    DWORD *lpdwTagId,
+    const BYTE *lpDependencies,
+    DWORD dwDependenciesSize,
+    LPCWSTR lpServiceStartName,
+    const BYTE *lpPassword,
+    DWORD dwPasswordSize,
+    POLICY_HANDLE *phService)
+{
+    service_entry *entry;
+    DWORD err;
+
+    WINE_TRACE("conn=%p (%s, %s, %s)\n", rpc_handle, wine_dbgstr_guid(&hSCManager->uuid), wine_dbgstr_w(lpServiceName), wine_dbgstr_w(lpBinaryPathName));
+    if ((err = validate_scm_handle(hSCManager, SC_MANAGER_CREATE_SERVICE)) != 0)
+        return err;
+
+    if (!validate_service_name(lpServiceName))
+        return ERROR_INVALID_NAME;
+    if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize) || !lpServiceName[0] || !lpBinaryPathName[0])
+        return ERROR_INVALID_PARAMETER;
+
+    if (lpPassword)
+        WINE_FIXME("Don't know how to add a password\n");   /* I always get ERROR_GEN_FAILURE */
+    if (lpDependencies)
+        WINE_FIXME("Dependencies not supported yet\n");
+    
+    entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*entry));
+    entry->name = strdupW(lpServiceName);
+    entry->config.dwServiceType = dwServiceType;
+    entry->config.dwStartType = dwStartType;
+    entry->config.dwErrorControl = dwErrorControl;
+    entry->config.lpBinaryPathName = strdupW(lpBinaryPathName);
+    entry->config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
+    entry->config.lpServiceStartName = strdupW(lpServiceStartName);
+    entry->config.lpDisplayName = strdupW(lpDisplayName);
+
+    if (lpdwTagId)      /* TODO: in most situations a non-NULL tagid will generate a ERROR_INVALID_PARAMETER */
+        entry->config.dwTagId = *lpdwTagId;
+    else
+        entry->config.dwTagId = 0;
+
+    /* other fields NULL*/
+
+    if (!validate_service_config(entry))
+    {
+        WINE_ERR("Invalid data while trying to create service\n");
+        free_service_entry(entry);
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    lock_services();
+
+    if (find_service(lpServiceName))
+    {
+        unlock_services();
+        free_service_entry(entry);
+        return ERROR_SERVICE_EXISTS;
+    }
+
+    if (find_service_by_displayname(get_display_name(entry)))
+    {
+        unlock_services();
+        free_service_entry(entry);
+        return ERROR_DUPLICATE_SERVICE_NAME;
+    }
+    
+    err = add_service(entry);
+    if (err != ERROR_SUCCESS)
+    {
+        unlock_services();
+        free_service_entry(entry);
+        return err;
+    }
+    unlock_services();
+
+    return create_handle_for_service(entry, dwDesiredAccess, phService);
+}
+
+DWORD svcctl_DeleteService(
+    SvcCtlRpcHandle rpc_handle,
+    POLICY_HANDLE *hService)
+{
+    service_entry *service;
+    DWORD err;
+
+    if ((err = validate_service_handle(hService, DELETE, &service)) != 0)
+        return err;
+
+    lock_services();
+    
+    if (!is_marked_for_delete(service))
+        err = remove_service(service);
+    else
+        err = ERROR_SERVICE_MARKED_FOR_DELETE;
+
+    unlock_services();
+
+    return err;
+}
+
 DWORD svcctl_CloseServiceHandle(
     SvcCtlRpcHandle rpc_handle,
     POLICY_HANDLE *handle)
@@ -181,7 +373,11 @@ DWORD svcctl_CloseServiceHandle(
     if ((err = validate_policy_handle(handle, HANDLE_TYPE_DONT_CARE, 0, &data)) != ERROR_SUCCESS)
         return err;
     
+    if (data->service)
+        release_service(data->service);
+
     RtlFreeHandle(&g_handle_table, (RTL_HANDLE *)data);
+
     ZeroMemory(handle, sizeof(*handle));
     LeaveCriticalSection(&g_handle_table_cs);
     return ERROR_SUCCESS;
diff --git a/programs/services/services.c b/programs/services/services.c
index aaa55c5..b78fc5f 100644
--- a/programs/services/services.c
+++ b/programs/services/services.c
@@ -26,7 +26,6 @@
 #include <winsvc.h>
 #include <rpc.h>
 
-#include "wine/list.h"
 #include "wine/unicode.h"
 #include "wine/debug.h"
 #include "svcctl.h"
@@ -41,6 +40,18 @@ HANDLE g_hStartedEvent;
 
 struct list g_services;
 
+static CRITICAL_SECTION services_list_cs;
+static CRITICAL_SECTION_DEBUG services_list_cs_debug =
+{
+    0, 0, &services_list_cs,
+    { &services_list_cs_debug.ProcessLocksList, 
+      &services_list_cs_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": services_list_cs") }
+};
+static CRITICAL_SECTION services_list_cs = { &services_list_cs_debug, -1, 0, 0, 0, 0 };
+
+static const WCHAR SZ_LOCAL_SYSTEM[] = {'L','o','c','a','l','S','y','s','t','e','m',0};
+
 /* 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','\\',
@@ -115,6 +126,104 @@ DWORD load_service_config(HKEY hKey, service_entry *entry)
     return ERROR_SUCCESS;
 }
 
+static DWORD reg_set_string_value(HKEY hKey, LPCWSTR value_name, LPCWSTR string)
+{
+    if (!string)
+    {
+        DWORD err;
+        err = RegDeleteValueW(hKey, value_name);
+        if (err != ERROR_FILE_NOT_FOUND)
+            return err;
+
+        return ERROR_SUCCESS;
+    }
+    
+    return RegSetValueExW(hKey, value_name, 0, REG_SZ, (LPBYTE)string, sizeof(WCHAR)*(strlenW(string) + 1));
+}
+
+static DWORD save_service_config(service_entry *entry)
+{
+    HKEY hServicesRootKey;
+    DWORD err;
+    HKEY hKey = NULL;
+
+    if ((err = RegCreateKeyW(HKEY_LOCAL_MACHINE, SZ_SERVICES_KEY, &hServicesRootKey)) != 0)
+        goto cleanup;
+
+    err = RegCreateKeyW(hServicesRootKey, entry->name, &hKey);
+    RegCloseKey(hServicesRootKey);
+    if (err != ERROR_SUCCESS)
+        goto cleanup;
+
+    if ((err = reg_set_string_value(hKey, SZ_DISPLAY_NAME, entry->config.lpDisplayName)) != 0)
+        goto cleanup;
+    if ((err = reg_set_string_value(hKey, SZ_IMAGE_PATH, entry->config.lpBinaryPathName)) != 0)
+        goto cleanup;
+    if ((err = reg_set_string_value(hKey, SZ_GROUP, entry->config.lpLoadOrderGroup)) != 0)
+        goto cleanup;
+    if ((err = reg_set_string_value(hKey, SZ_OBJECT_NAME, entry->config.lpServiceStartName)) != 0)
+        goto cleanup;
+    if ((err = reg_set_string_value(hKey, SZ_DESCRIPTION, entry->description)) != 0)
+        goto cleanup;
+    if ((err = RegSetValueExW(hKey, SZ_START, 0, REG_DWORD, (LPBYTE)&entry->config.dwStartType, sizeof(DWORD))) != 0)
+        goto cleanup;
+    if ((err = RegSetValueExW(hKey, SZ_ERROR, 0, REG_DWORD, (LPBYTE)&entry->config.dwErrorControl, sizeof(DWORD))) != 0)
+        goto cleanup;
+
+    if ((err = RegSetValueExW(hKey, SZ_TYPE, 0, REG_DWORD, (LPBYTE)&entry->config.dwServiceType, sizeof(DWORD))) != 0)
+        goto cleanup;
+
+    if (entry->config.dwTagId)
+        err = RegSetValueExW(hKey, SZ_TAG, 0, REG_DWORD, (LPBYTE)&entry->config.dwTagId, sizeof(DWORD));
+    else
+        err = RegDeleteValueW(hKey, SZ_TAG);
+
+    if (err != 0 && err != ERROR_FILE_NOT_FOUND)
+        goto cleanup;
+
+    err = ERROR_SUCCESS;
+cleanup:
+    RegCloseKey(hKey);
+    return err;
+}
+
+DWORD add_service(service_entry *service)
+{
+    int err;
+    if ((err = save_service_config(service)) != ERROR_SUCCESS)
+    {
+        WINE_ERR("Couldn't store service configuration: error %u\n", err);
+        return ERROR_GEN_FAILURE;
+    }
+
+    list_add_tail(&g_services, &service->entry);
+    return ERROR_SUCCESS;
+}
+
+DWORD remove_service(service_entry *service)
+{
+    int err;
+    HKEY hKey;
+
+    if ((err = RegOpenKeyW(HKEY_LOCAL_MACHINE, SZ_SERVICES_KEY, &hKey)) != 0)
+        return err;
+
+    err = RegDeleteTreeW(hKey, service->name);
+    RegCloseKey(hKey);
+
+    if (err != 0)
+        return err;
+
+    list_remove(&service->entry);
+    service->entry.next = service->entry.prev = NULL;
+    return ERROR_SUCCESS;
+}
+
+BOOL validate_service_name(LPCWSTR name)
+{
+    return (name && name[0] && !strchrW(name, '/') && !strchrW(name, '\\'));
+}
+
 BOOL validate_service_config(service_entry *entry)
 {
     if (entry->config.dwServiceType & SERVICE_WIN32 && (entry->config.lpBinaryPathName == NULL || !entry->config.lpBinaryPathName[0]))
@@ -122,9 +231,92 @@ BOOL validate_service_config(service_entry *entry)
         WINE_ERR("Service %s is Win32 but have no image path set\n", wine_dbgstr_w(entry->name));
         return FALSE;
     }
+
+    switch (entry->config.dwServiceType)
+    {
+    case SERVICE_KERNEL_DRIVER:
+    case SERVICE_FILE_SYSTEM_DRIVER:
+    case SERVICE_WIN32_OWN_PROCESS:
+    case SERVICE_WIN32_SHARE_PROCESS:
+        /* No problem */
+        break;
+    case SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS:
+    case SERVICE_WIN32_SHARE_PROCESS | SERVICE_INTERACTIVE_PROCESS:
+        /* These can be only run as LocalSystem */
+        if (entry->config.lpServiceStartName && strcmpiW(entry->config.lpServiceStartName, SZ_LOCAL_SYSTEM) != 0)
+        {
+            WINE_ERR("Service %s is interactive but has a start name\n", wine_dbgstr_w(entry->name));
+            return FALSE;
+        }
+        break;
+    default:
+        WINE_ERR("Service %s have unknown service type\n", wine_dbgstr_w(entry->name));
+        return FALSE;
+    }
+
+    /* StartType can only be a single value (if several values are mixed the result is probably not what was intended) */
+    if (entry->config.dwStartType > SERVICE_DISABLED)
+    {
+        WINE_ERR("Service %s have unknown start type\n", wine_dbgstr_w(entry->name));
+        return FALSE;
+    }
+
+    /* SERVICE_BOOT_START and SERVICE_SYSTEM_START or only allowed for driver services */
+    if (((entry->config.dwStartType == SERVICE_BOOT_START) || (entry->config.dwStartType == SERVICE_SYSTEM_START)) &&
+        ((entry->config.dwServiceType & SERVICE_WIN32_OWN_PROCESS) || (entry->config.dwServiceType & SERVICE_WIN32_SHARE_PROCESS)))
+    {
+        WINE_ERR("Service %s - SERVICE_BOOT_START and SERVICE_SYSTEM_START are only allowed for driver services\n", wine_dbgstr_w(entry->name));
+        return FALSE;
+    }
+
+    if (entry->config.lpServiceStartName == NULL)
+        entry->config.lpServiceStartName = strdupW(SZ_LOCAL_SYSTEM); 
+
     return TRUE;
 }
 
+void lock_services()
+{
+    EnterCriticalSection(&services_list_cs);    
+}
+
+void unlock_services()
+{
+    LeaveCriticalSection(&services_list_cs);    
+}
+
+service_entry *find_service(LPCWSTR name)
+{
+    service_entry *service;
+
+    LIST_FOR_EACH_ENTRY(service, &g_services, service_entry, entry)
+    {
+        if (strcmpiW(name, service->name) == 0)
+            return service;
+    }
+
+    return NULL;
+}
+
+service_entry *find_service_by_displayname(LPCWSTR name)
+{
+    service_entry *service;
+
+    LIST_FOR_EACH_ENTRY(service, &g_services, service_entry, entry)
+    {
+        if (strcmpiW(name, service->config.lpDisplayName) == 0)
+            return service;
+    }
+
+    return NULL;
+}
+
+void release_service(service_entry *service)
+{
+    if (InterlockedDecrement(&service->ref_count) == 0 && is_marked_for_delete(service))
+        free_service_entry(service);
+}
+
 DWORD load_services()
 {
     HKEY hServicesRootKey;
diff --git a/programs/services/services.h b/programs/services/services.h
index 3ffe37c..5dfca89 100644
--- a/programs/services/services.h
+++ b/programs/services/services.h
@@ -26,6 +26,7 @@
 typedef struct _service_entry
 {
     struct list entry;
+    LONG ref_count;                    /* number of references - if goes to zero and the service is deleted the structure will be freed */
     LPWSTR name;
     SERVICE_STATUS_PROCESS status;
     QUERY_SERVICE_CONFIGW config;
@@ -34,6 +35,18 @@ typedef struct _service_entry
     LPWSTR dependOnGroups;
 } service_entry;
 
+BOOL validate_service_name(LPCWSTR name);
+BOOL validate_service_config(service_entry *entry);
+service_entry *find_service(LPCWSTR name);
+service_entry *find_service_by_displayname(LPCWSTR name);
+DWORD add_service(service_entry *entry);
+DWORD remove_service(service_entry *entry);
+void free_service_entry(service_entry *entry);
+void release_service(service_entry *service);
+
+void lock_services();
+void unlock_services();
+
 extern HANDLE g_hStartedEvent;
 
 DWORD RPC_MainLoop(void);
@@ -41,8 +54,20 @@ DWORD RPC_MainLoop(void);
 /* from utils.c */
 LPWSTR strdupW(LPCWSTR str);
 
+BOOL check_multisz(LPCWSTR lpMultiSz, DWORD cbSize);
+
 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);
 
+static inline LPCWSTR get_display_name(service_entry *service)
+{
+    return service->config.lpDisplayName ? service->config.lpDisplayName : service->name;
+}
+
+static inline BOOL is_marked_for_delete(service_entry *service)
+{
+    return service->entry.next == NULL;
+}
+
 #endif /*WINE_PROGRAMS_UTILS_H_*/
diff --git a/programs/services/utils.c b/programs/services/utils.c
index cc03c45..7c9dc4a 100644
--- a/programs/services/utils.c
+++ b/programs/services/utils.c
@@ -31,14 +31,30 @@ WINE_DEFAULT_DEBUG_CHANNEL(service);
 
 LPWSTR strdupW(LPCWSTR str)
 {
-    int len = strlenW(str);
-    WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
+    int len;
+    WCHAR *buf;
+
+    if (str == NULL)
+        return NULL;
+    len = strlenW(str);
+    buf = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
     if (buf == NULL)
         return NULL;
     strcpyW(buf, str);
     return buf;
 }
 
+BOOL check_multisz(LPCWSTR lpMultiSz, DWORD cbSize)
+{
+    if (cbSize == 0 || (cbSize == sizeof(WCHAR) && lpMultiSz[0] == 0))
+        return TRUE;
+    if ((cbSize % sizeof(WCHAR)) != 0 || cbSize < 2*sizeof(WCHAR))
+        return FALSE;
+    if (lpMultiSz[cbSize/2 - 1] || lpMultiSz[cbSize/2 - 2])
+        return FALSE;
+    return TRUE;
+}
+
 DWORD load_reg_string(HKEY hKey, LPCWSTR szValue, BOOL bExpand, LPWSTR *output)
 {
     DWORD size, type;
-- 
1.5.4


--------------030905090709060309030007--



More information about the wine-patches mailing list