services: Move CreateService, OpenService and DeleteService implementations from advapi32.dll to services.exe.

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


---
 dlls/advapi32/service.c      |  232 +++++++++++-------------------------------
 include/wine/svcctl.idl      |   32 ++++++
 programs/services/rpc.c      |  220 +++++++++++++++++++++++++++++++++++++++
 programs/services/services.c |  198 +++++++++++++++++++++++++++++++++++-
 programs/services/services.h |   25 +++++
 programs/services/utils.c    |   20 ++++-
 6 files changed, 548 insertions(+), 179 deletions(-)

diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c
index 3855b3c..a22c309 100644
--- a/dlls/advapi32/service.c
+++ b/dlls/advapi32/service.c
@@ -1198,10 +1198,7 @@ CloseServiceHandle( SC_HANDLE hSCObject )
     }
 
     obj = (struct sc_handle *)hSCObject;
-    if (obj->server_handle)   /* service handles currently don't have RPC connections */
-        err = svcctl_CloseServiceHandle(&obj->server_handle);
-    else
-        err = ERROR_SUCCESS;
+    err = svcctl_CloseServiceHandle(&obj->server_handle);
     sc_handle_free( obj );
 
     if (err != ERROR_SUCCESS)
@@ -1252,8 +1249,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;
 
@@ -1271,13 +1267,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,
@@ -1285,18 +1274,31 @@ 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;
+    
+    err = svcctl_OpenServiceW(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);
 
@@ -1317,15 +1319,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));
@@ -1343,164 +1339,55 @@ CreateServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
         return NULL;
     }
 
-    if (!(hscm->dwAccess & SC_MANAGER_CREATE_SERVICE))
-    {
-        SetLastError(ERROR_ACCESS_DENIED);
-        return NULL;
-    }
-
-    if (!lpServiceName[0])
-    {
-        SetLastError(ERROR_INVALID_NAME);
-        return NULL;
-    }
-
-    if (!lpBinaryPathName[0])
+    if (lpDependencies)
     {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return NULL;
+        const WCHAR *wptr = lpDependencies;
+        while (*wptr)
+            wptr += strlenW(wptr)+1;
+        depslen = (wptr - lpDependencies + 1)*sizeof(WCHAR);
     }
+    else
+        depslen = 0;
 
-    /* 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->scm = hscm;
+    hscm->hdr.ref_count++;
 
-    if (displayname_exists)
-    {
-        SetLastError(ERROR_DUPLICATE_SERVICE_NAME);
-        return NULL;
-    }
+    err = svcctl_CreateServiceW(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;
 }
 
 
@@ -1563,6 +1450,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)
@@ -1570,21 +1458,17 @@ BOOL WINAPI DeleteService( SC_HANDLE hService )
         SetLastError( ERROR_INVALID_HANDLE );
         return FALSE;
     }
-
-    if (!(hsvc->dwAccess & DELETE))
+    
+    err = svcctl_DeleteService(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 6c61fe8..d4e7081 100644
--- a/include/wine/svcctl.idl
+++ b/include/wine/svcctl.idl
@@ -48,6 +48,31 @@ interface svcctl
         [in,out] SC_RPC_HANDLE *handle
     );
 
+    /* Compatible with Windows function 0x02 */
+    DWORD svcctl_DeleteService(
+        [in] SC_RPC_HANDLE hService
+    );
+
+    /* Compatible with Windows function 0x0c */
+    DWORD svcctl_CreateServiceW(
+        [in] SC_RPC_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] SC_RPC_HANDLE *phService
+    );
+
     /* Compatible with Windows function 0x0f */
     DWORD svcctl_OpenSCManagerW(
         [in,unique] MACHINE_HANDLEW MachineName,
@@ -56,4 +81,11 @@ interface svcctl
         [out] SC_RPC_HANDLE *handle
     );
 
+    /* Compatible with Windows function 0x10 */
+    DWORD svcctl_OpenServiceW(
+        [in] SC_RPC_HANDLE hSCManager,
+        [in] LPCWSTR lpServiceName,
+        [in] DWORD dwDesiredAccess,
+        [out] SC_RPC_HANDLE *phService
+    );
 }
diff --git a/programs/services/rpc.c b/programs/services/rpc.c
index f514952..8ef1e92 100644
--- a/programs/services/rpc.c
+++ b/programs/services/rpc.c
@@ -56,6 +56,14 @@ 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 enum
 {
     SC_HTYPE_DONT_CARE = 0,
@@ -74,6 +82,51 @@ struct sc_manager       /* service control manager handle */
     struct sc_handle hdr;
 };
 
+struct sc_service       /* service handle */
+{
+    struct sc_handle hdr;
+    struct service_entry *service_entry;
+};
+
+/* Check if the given handle is of the required type and allows the requested access. */
+static DWORD validate_context_handle(SC_RPC_HANDLE handle, DWORD type, DWORD needed_access, struct sc_handle **out_hdr)
+{
+    struct sc_handle *hdr = (struct sc_handle *)handle;
+
+    if (type != SC_HTYPE_DONT_CARE && hdr->type != type)
+    {
+        WINE_ERR("Handle is of an invalid type (%d, %d)\n", hdr->type, type);
+        return ERROR_INVALID_HANDLE;
+    }
+
+    if ((needed_access & hdr->access) != needed_access)
+    {
+        WINE_ERR("Access denied - handle created with access %x, needed %x\n", hdr->access, needed_access);
+        return ERROR_ACCESS_DENIED;
+    }
+    
+    *out_hdr = hdr;
+    return ERROR_SUCCESS;
+}
+
+static DWORD validate_scm_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_manager **manager)
+{
+    struct sc_handle *hdr;
+    DWORD err = validate_context_handle(handle, SC_HTYPE_MANAGER, needed_access, &hdr);
+    if (err == ERROR_SUCCESS)
+        *manager = (struct sc_manager *)hdr;
+    return err;
+}
+
+static DWORD validate_service_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_service **service)
+{
+    struct sc_handle *hdr;
+    DWORD err = validate_context_handle(handle, SC_HTYPE_SERVICE, needed_access, &hdr);
+    if (err == ERROR_SUCCESS)
+        *service = (struct sc_service *)hdr;
+    return err;
+}
+
 DWORD svcctl_OpenSCManagerW(
     MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */
     LPCWSTR DatabaseName,
@@ -117,12 +170,179 @@ static void SC_RPC_HANDLE_destroy(SC_RPC_HANDLE handle)
             HeapFree(GetProcessHeap(), 0, manager);
             break;
         }
+        case SC_HTYPE_SERVICE:
+        {
+            struct sc_service *service = (struct sc_service *)hdr;
+            release_service(service->service_entry);
+            HeapFree(GetProcessHeap(), 0, service);
+            break;
+        }
         default:
             WINE_ERR("invalid handle type %d\n", hdr->type);
             RpcRaiseException(ERROR_INVALID_HANDLE);
     }
 }
 
+static DWORD create_handle_for_service(struct service_entry *entry, DWORD dwDesiredAccess, SC_RPC_HANDLE *phService)
+{
+    struct sc_service *service;
+
+    if (!(service = HeapAlloc(GetProcessHeap(), 0, sizeof(*service))))
+    {
+        release_service(entry);
+        return ERROR_NOT_ENOUGH_SERVER_MEMORY;
+    }
+
+    service->hdr.type = SC_HTYPE_SERVICE;
+    service->hdr.access = dwDesiredAccess;
+    RtlMapGenericMask(&service->hdr.access, &g_svc_generic);
+    service->service_entry = entry;
+    if (dwDesiredAccess & MAXIMUM_ALLOWED)
+        dwDesiredAccess |= SERVICE_ALL_ACCESS;
+
+    *phService = &service->hdr;
+    return ERROR_SUCCESS;
+}
+
+DWORD svcctl_OpenServiceW(
+    SC_RPC_HANDLE hSCManager,
+    LPCWSTR lpServiceName,
+    DWORD dwDesiredAccess,
+    SC_RPC_HANDLE *phService)
+{
+    struct sc_manager *manager;
+    struct service_entry *entry;
+    DWORD err;
+
+    WINE_TRACE("(%s, 0x%x)\n", wine_dbgstr_w(lpServiceName), dwDesiredAccess);
+
+    if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
+        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(
+    SC_RPC_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,
+    SC_RPC_HANDLE *phService)
+{
+    struct sc_manager *manager;
+    struct service_entry *entry;
+    DWORD err;
+
+    WINE_TRACE("(%s, %s, 0x%x, %s)\n", wine_dbgstr_w(lpServiceName), wine_dbgstr_w(lpDisplayName), dwDesiredAccess, wine_dbgstr_w(lpBinaryPathName));
+
+    if ((err = validate_scm_handle(hSCManager, SC_MANAGER_CREATE_SERVICE, &manager)) != ERROR_SUCCESS)
+        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(
+    SC_RPC_HANDLE hService)
+{
+    struct sc_service *service;
+    DWORD err;
+
+    if ((err = validate_service_handle(hService, DELETE, &service)) != ERROR_SUCCESS)
+        return err;
+
+    lock_services();
+    
+    if (!is_marked_for_delete(service->service_entry))
+        err = remove_service(service->service_entry);
+    else
+        err = ERROR_SERVICE_MARKED_FOR_DELETE;
+
+    unlock_services();
+
+    return err;
+}
+
 DWORD svcctl_CloseServiceHandle(
     SC_RPC_HANDLE *handle)
 {
diff --git a/programs/services/services.c b/programs/services/services.c
index ebd0f53..2ce52de 100644
--- a/programs/services/services.c
+++ b/programs/services/services.c
@@ -25,7 +25,6 @@
 #include <winsvc.h>
 #include <rpc.h>
 
-#include "wine/list.h"
 #include "wine/unicode.h"
 #include "wine/debug.h"
 #include "svcctl.h"
@@ -40,6 +39,18 @@ HANDLE g_hStartedEvent;
 
 static 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','\\',
@@ -59,7 +70,7 @@ 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};
 
 
-static void free_service_entry(struct service_entry *entry)
+void free_service_entry(struct service_entry *entry)
 {
     HeapFree(GetProcessHeap(), 0, entry->name);
     HeapFree(GetProcessHeap(), 0, entry->config.lpBinaryPathName);
@@ -114,16 +125,197 @@ static DWORD load_service_config(HKEY hKey, struct service_entry *entry)
     return ERROR_SUCCESS;
 }
 
-static BOOL validate_service_config(struct service_entry *entry)
+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(struct 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(struct 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(struct 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(struct 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;
     }
+
+    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(void)
+{
+    EnterCriticalSection(&services_list_cs);    
+}
+
+void unlock_services(void)
+{
+    LeaveCriticalSection(&services_list_cs);    
+}
+
+struct service_entry *find_service(LPCWSTR name)
+{
+    struct service_entry *service;
+
+    LIST_FOR_EACH_ENTRY(service, &g_services, struct service_entry, entry)
+    {
+        if (strcmpiW(name, service->name) == 0)
+            return service;
+    }
+
+    return NULL;
+}
+
+struct service_entry *find_service_by_displayname(LPCWSTR name)
+{
+    struct service_entry *service;
+
+    LIST_FOR_EACH_ENTRY(service, &g_services, struct service_entry, entry)
+    {
+        if (strcmpiW(name, service->config.lpDisplayName) == 0)
+            return service;
+    }
+
+    return NULL;
+}
+
+void release_service(struct service_entry *service)
+{
+    if (InterlockedDecrement(&service->ref_count) == 0 && is_marked_for_delete(service))
+        free_service_entry(service);
+}
+
 static DWORD load_services(void)
 {
     HKEY hServicesRootKey;
diff --git a/programs/services/services.h b/programs/services/services.h
index 7b7c3c1..a8700c6 100644
--- a/programs/services/services.h
+++ b/programs/services/services.h
@@ -26,6 +26,7 @@
 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 @@ struct service_entry
     LPWSTR dependOnGroups;
 };
 
+BOOL validate_service_name(LPCWSTR name);
+BOOL validate_service_config(struct service_entry *entry);
+struct service_entry *find_service(LPCWSTR name);
+struct service_entry *find_service_by_displayname(LPCWSTR name);
+DWORD add_service(struct service_entry *entry);
+DWORD remove_service(struct service_entry *entry);
+void free_service_entry(struct service_entry *entry);
+void release_service(struct service_entry *service);
+
+void lock_services(void);
+void unlock_services(void);
+
 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(struct service_entry *service)
+{
+    return service->config.lpDisplayName ? service->config.lpDisplayName : service->name;
+}
+
+static inline BOOL is_marked_for_delete(struct 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.3.GIT



--------------050502050005010302020107--



More information about the wine-patches mailing list