ADVAPI32: convert services to use pipes for RPC instead of shared memory (RESEND)

Mike McCormack mike at codeweavers.com
Wed Jan 19 00:45:20 CST 2005


(This patch is big, but it's difficult to break up because the 
communication mechanism is being changed.  I've also made ControlService 
work this time.)

This patch uses pipes for communication between processes interacting 
with services and the services processes themselves.  Using a pipe we 
can serialize requests, and make the service code more immune to race 
conditions.

Mike


ChangeLog:
* convert services to use pipes for RPC instead of shared memory
-------------- next part --------------
Index: dlls/advapi32/service.c
===================================================================
RCS file: /home/wine/wine/dlls/advapi32/service.c,v
retrieving revision 1.82
diff -u -p -r1.82 service.c
--- dlls/advapi32/service.c	14 Jan 2005 16:50:57 -0000	1.82
+++ dlls/advapi32/service.c	19 Jan 2005 06:41:15 -0000
@@ -22,6 +22,7 @@
 #include <stdarg.h>
 #include <string.h>
 #include <time.h>
+#include <assert.h>
 
 #include "windef.h"
 #include "winbase.h"
@@ -39,31 +40,44 @@ static const WCHAR szServiceManagerKey[]
       'S','e','r','v','i','c','e','s','\\',0 };
 static const WCHAR  szSCMLock[] = {'A','D','V','A','P','I','_','S','C','M',
                                    'L','O','C','K',0};
-static const WCHAR  szServiceShmemNameFmtW[] = {'A','D','V','A','P','I','_',
-                                                'S','E','B','_','%','s',0};
-static const WCHAR  szServiceDispEventNameFmtW[] = {'A','D','V','A','P','I','_',
-                                                    'D','I','S','P','_','%','s',0};
-static const WCHAR  szServiceMutexNameFmtW[] = {'A','D','V','A','P','I','_',
-                                                'M','U','X','_','%','s',0};
-static const WCHAR  szServiceAckEventNameFmtW[] = {'A','D','V','A','P','I','_',
-                                                   'A','C','K','_','%','s',0};
-static const WCHAR  szWaitServiceStartW[]  = {'A','D','V','A','P','I','_','W',
-                                              'a','i','t','S','e','r','v','i',
-                                              'c','e','S','t','a','r','t',0};
-
-struct SEB              /* service environment block */
-{                       /*   resides in service's shared memory object */
-    DWORD control_code;      /* service control code */
-    DWORD dispatcher_error;  /* set by dispatcher if it fails to invoke control handler */
+
+typedef struct service_start_info_t
+{
+    DWORD cmd;
+    DWORD size;
+    WCHAR str[1];
+} service_start_info;
+
+#define WINESERV_STARTINFO   1
+#define WINESERV_GETSTATUS   2
+#define WINESERV_SENDCONTROL 3
+
+typedef struct service_data_t
+{
+    struct service_data_t *next;
+    LPHANDLER_FUNCTION handler;
     SERVICE_STATUS status;
-    DWORD argc;
-    /* variable part of SEB contains service arguments */
+    HANDLE thread;
+    BOOL unicode;
+    union {
+        LPSERVICE_MAIN_FUNCTIONA a;
+        LPSERVICE_MAIN_FUNCTIONW w;
+    } proc;
+    LPWSTR args;
+    WCHAR name[1];
+} service_data;
+
+static CRITICAL_SECTION service_cs;
+static CRITICAL_SECTION_DEBUG service_cs_debug =
+{
+    0, 0, &service_cs,
+    { &service_cs_debug.ProcessLocksList, 
+      &service_cs_debug.ProcessLocksList },
+      0, 0, { 0, (DWORD)(__FILE__ ": service_cs") }
 };
+static CRITICAL_SECTION service_cs = { &service_cs_debug, -1, 0, 0, 0, 0 };
 
-static HANDLE dispatcher_event;  /* this is used by service thread to wakeup
-                                  * service control dispatcher when thread terminates */
-
-static struct service_thread_data *service;  /* FIXME: this should be a list */
+service_data *service_list;
 
 /******************************************************************************
  * SC_HANDLEs
@@ -171,7 +185,7 @@ static inline LPWSTR SERV_dup( LPCSTR st
     return wstr;
 }
 
-static inline LPWSTR SERV_dupmulti( LPCSTR str )
+static inline LPWSTR SERV_dupmulti(LPCSTR str)
 {
     UINT len = 0, n = 0;
     LPWSTR wstr;
@@ -195,372 +209,531 @@ static inline VOID SERV_free( LPWSTR wst
     HeapFree( GetProcessHeap(), 0, wstr );
 }
 
+
 /******************************************************************************
- * read_scm_lock_data
- *
- * helper function for service control dispatcher
+ * Service IPC functions
+ */
+static LPWSTR service_get_pipe_name(LPWSTR service)
+{
+    static const WCHAR prefix[] = { '\\','\\','.','\\','p','i','p','e','\\',
+                   '_','_','w','i','n','e','s','e','r','v','i','c','e','_',0};
+    LPWSTR name;
+    DWORD len;
+
+    len = sizeof prefix + strlenW(service)*sizeof(WCHAR);
+    name = HeapAlloc(GetProcessHeap(), 0, len);
+    strcpyW(name, prefix);
+    strcatW(name, service);
+    return name;
+}
+
+static HANDLE service_open_pipe(LPWSTR service)
+{
+    LPWSTR szPipe = service_get_pipe_name( service );
+    HANDLE handle = INVALID_HANDLE_VALUE;
+
+    do {
+        handle = CreateFileW(szPipe, GENERIC_READ|GENERIC_WRITE,
+                         0, NULL, OPEN_ALWAYS, 0, NULL);
+        if (handle != INVALID_HANDLE_VALUE)
+            break;
+        if (GetLastError() != ERROR_PIPE_BUSY)
+            break;
+    } while (WaitNamedPipeW(szPipe, NMPWAIT_WAIT_FOREVER));
+    SERV_free(szPipe);
+
+    return handle;
+}
+
+/******************************************************************************
+ * service_get_event_handle
+ */
+static HANDLE service_get_event_handle(LPWSTR service)
+{
+    static const WCHAR prefix[] = { 
+           '_','_','w','i','n','e','s','e','r','v','i','c','e','_',0};
+    LPWSTR name;
+    DWORD len;
+    HANDLE handle;
+
+    len = sizeof prefix + strlenW(service)*sizeof(WCHAR);
+    name = HeapAlloc(GetProcessHeap(), 0, len);
+    strcpyW(name, prefix);
+    strcatW(name, service);
+    handle = CreateEventW(NULL, TRUE, FALSE, name);
+    SERV_free(name);
+    return handle;
+}
+
+/******************************************************************************
+ * service_thread
  *
- * SCM database is locked by StartService;
- * open global SCM lock object and read service name
+ * Call into the main service routine provided by StartServiceCtrlDispatcher.
  */
-static BOOL read_scm_lock_data( LPWSTR buffer )
+static DWORD WINAPI service_thread(LPVOID arg)
 {
-    HANDLE hLock;
-    LPWSTR argptr;
+    service_data *info = arg;
+    LPWSTR str = info->args;
+    DWORD argc = 0, len = 0;
+
+    TRACE("%p\n", arg);
 
-    hLock = OpenFileMappingW( FILE_MAP_ALL_ACCESS, FALSE, szSCMLock );
-    if( NULL == hLock )
+    while (str[len])
     {
-        SetLastError( ERROR_FAILED_SERVICE_CONTROLLER_CONNECT );
-        return FALSE;
+        len += strlenW(&str[len]) + 1;
+        argc++;
     }
-    argptr = MapViewOfFile( hLock, FILE_MAP_ALL_ACCESS,
-                            0, 0, MAX_SERVICE_NAME * sizeof(WCHAR) );
-    if( NULL == argptr )
+    
+    if (info->unicode)
     {
-        CloseHandle( hLock );
-        return FALSE;
+        LPWSTR *argv, p;
+
+        argv = HeapAlloc(GetProcessHeap(), 0, (argc+1)*sizeof(LPWSTR));
+        for (argc=0, p=str; *p; p += strlenW(p) + 1)
+            argv[argc++] = p;
+        argv[argc] = NULL;
+
+        info->proc.w(argc, argv);
+        HeapFree(GetProcessHeap(), 0, argv);
     }
-    strcpyW( buffer, argptr );
-    UnmapViewOfFile( argptr );
-    CloseHandle( hLock );
-    return TRUE;
+    else
+    {
+        LPSTR strA, *argv, p;
+        DWORD lenA;
+        
+        lenA = WideCharToMultiByte(CP_ACP,0, str, len, NULL, 0, NULL, NULL);
+        strA = HeapAlloc(GetProcessHeap(), 0, lenA);
+        WideCharToMultiByte(CP_ACP,0, str, len, strA, lenA, NULL, NULL);
+
+        argv = HeapAlloc(GetProcessHeap(), 0, (argc+1)*sizeof(LPSTR));
+        for (argc=0, p=strA; *p; p += strlen(p) + 1)
+            argv[argc++] = p;
+        argv[argc] = NULL;
+
+        info->proc.a(argc, argv);
+        HeapFree(GetProcessHeap(), 0, argv);
+        HeapFree(GetProcessHeap(), 0, strA);
+    }
+    return 0;
 }
 
 /******************************************************************************
- * open_seb_shmem
- *
- * helper function for service control dispatcher
+ * service_handle_start
  */
-static struct SEB* open_seb_shmem( LPWSTR service_name, HANDLE* hServiceShmem )
+static BOOL service_handle_start(HANDLE pipe, service_data *service, DWORD count)
 {
-    WCHAR object_name[ MAX_PATH ];
-    HANDLE hmem;
-    struct SEB *ret;
+    DWORD read = 0, result = 0;
+    LPWSTR args;
+    BOOL r;
 
-    snprintfW( object_name, MAX_PATH, szServiceShmemNameFmtW, service_name );
-    hmem = OpenFileMappingW( FILE_MAP_ALL_ACCESS, FALSE, object_name );
-    if( NULL == hmem )
-        return NULL;
+    TRACE("%p %p %ld\n", pipe, service, count);
 
-    ret = MapViewOfFile( hmem, FILE_MAP_ALL_ACCESS, 0, 0, 0 );
-    if( NULL == ret )
-        CloseHandle( hmem );
-    else
-        *hServiceShmem = hmem;
-    return ret;
+    args = HeapAlloc(GetProcessHeap(), 0, count*sizeof(WCHAR));
+    r = ReadFile(pipe, args, count*sizeof(WCHAR), &read, NULL);
+    if (!r || count!=read/sizeof(WCHAR) || args[count-1])
+    {
+        ERR("pipe read failed r = %d count = %ld/%ld args[n-1]=%s\n",
+            r, count, read/sizeof(WCHAR), debugstr_wn(args, count));
+        goto end;
+    }
+
+    if (service->thread)
+    {
+        ERR("service is not stopped\n");
+        goto end;
+    }
+
+    if (service->args)
+        SERV_free(service->args);
+    service->args = args;
+    args = NULL;
+    service->thread = CreateThread( NULL, 0, service_thread,
+                                    service, 0, NULL );
+
+end:
+    HeapFree(GetProcessHeap(), 0, args);
+    WriteFile( pipe, &result, sizeof result, &read, NULL );
+
+    return TRUE;
 }
 
 /******************************************************************************
- * build_arg_vectors
- *
- * helper function for start_new_service
- *
- * Allocate and initialize array of LPWSTRs to arguments in variable part
- * of service environment block.
- * First entry in the array is reserved for service name and not initialized.
+ * service_send_start_message
  */
-static LPWSTR* build_arg_vectors( struct SEB* seb )
+static BOOL service_send_start_message(HANDLE pipe, LPCWSTR *argv, DWORD argc)
 {
-    LPWSTR *ret;
-    LPWSTR argptr;
-    DWORD i;
+    DWORD i, len, count, result;
+    service_start_info *ssi;
+    LPWSTR p;
+    BOOL r;
 
-    ret = HeapAlloc( GetProcessHeap(), 0, (1 + seb->argc) * sizeof(LPWSTR) );
-    if( NULL == ret )
-        return NULL;
+    TRACE("%p %p %ld\n", pipe, argv, argc);
 
-    argptr = (LPWSTR) &seb[1];
-    for( i = 0; i < seb->argc; i++ )
+    /* calculate how much space do we need to send the startup info */
+    len = 1;
+    for (i=0; i<argc; i++)
+        len += strlenW(argv[i])+1;
+
+    ssi = HeapAlloc(GetProcessHeap(),0,sizeof *ssi + (len-1)*sizeof(WCHAR));
+    ssi->cmd = WINESERV_STARTINFO;
+    ssi->size = len;
+
+    /* copy service args into a single buffer*/
+    p = &ssi->str[0];
+    for (i=0; i<argc; i++)
     {
-        ret[ 1 + i ] = argptr;
-        argptr += 1 + strlenW( argptr );
+        strcpyW(p, argv[i]);
+        p += strlenW(p) + 1;
     }
-    return ret;
+    *p=0;
+
+    r = WriteFile(pipe, ssi, sizeof *ssi + len*sizeof(WCHAR), &count, NULL);
+    if (r)
+        r = ReadFile(pipe, &result, sizeof result, &count, NULL);
+
+    HeapFree(GetProcessHeap(),0,ssi);
+
+    return r;
 }
 
 /******************************************************************************
- * service thread
+ * service_handle_get_status
  */
-struct service_thread_data
+static BOOL service_handle_get_status(HANDLE pipe, service_data *service)
 {
-    WCHAR service_name[ MAX_SERVICE_NAME ];
-    CHAR service_nameA[ MAX_SERVICE_NAME ];
-    LPSERVICE_MAIN_FUNCTIONW service_main;
-    DWORD argc;
-    LPWSTR *argv;
-    HANDLE hServiceShmem;
-    struct SEB *seb;
-    HANDLE thread_handle;
-    HANDLE mutex;            /* provides serialization of control request */
-    HANDLE ack_event;        /* control handler completion acknowledgement */
-    LPHANDLER_FUNCTION ctrl_handler;
-};
+    DWORD count = 0;
+    TRACE("\n");
+    return WriteFile(pipe, &service->status, 
+                     sizeof service->status, &count, NULL);
+}
 
-static DWORD WINAPI service_thread( LPVOID arg )
+/******************************************************************************
+ * service_get_status
+ */
+static BOOL service_get_status(HANDLE pipe, LPSERVICE_STATUS status)
 {
-    struct service_thread_data *data = arg;
-
-    data->service_main( data->argc, data->argv );
-    SetEvent( dispatcher_event );
-    return 0;
+    DWORD cmd[2], count = 0;
+    BOOL r;
+    
+    cmd[0] = WINESERV_GETSTATUS;
+    cmd[1] = 0;
+    r = WriteFile( pipe, cmd, sizeof cmd, &count, NULL );
+    if (!r || count != sizeof cmd)
+    {
+        ERR("service protocol error - failed to write pipe!\n");
+        return r;
+    }
+    r = ReadFile( pipe, status, sizeof *status, &count, NULL );
+    if (!r || count != sizeof *status)
+        ERR("service protocol error - failed to read pipe "
+            "r = %d  count = %ld/%d!\n", r, count, sizeof *status);
+    return r;
 }
 
 /******************************************************************************
- * dispose_service_thread_data
- *
- * helper function for service control dispatcher
+ * service_send_control
  */
-static void dispose_service_thread_data( struct service_thread_data* thread_data )
+static BOOL service_send_control(HANDLE pipe, DWORD dwControl, DWORD *result)
 {
-    if( thread_data->mutex ) CloseHandle( thread_data->mutex );
-    if( thread_data->ack_event ) CloseHandle( thread_data->ack_event );
-    HeapFree( GetProcessHeap(), 0, thread_data->argv );
-    if( thread_data->seb ) UnmapViewOfFile( thread_data->seb );
-    if( thread_data->hServiceShmem ) CloseHandle( thread_data->hServiceShmem );
-    HeapFree( GetProcessHeap(), 0, thread_data );
+    DWORD cmd[2], count = 0;
+    BOOL r;
+    
+    cmd[0] = WINESERV_SENDCONTROL;
+    cmd[1] = dwControl;
+    r = WriteFile(pipe, cmd, sizeof cmd, &count, NULL);
+    if (!r || count != sizeof cmd)
+    {
+        ERR("service protocol error - failed to write pipe!\n");
+        return r;
+    }
+    r = ReadFile(pipe, result, sizeof *result, &count, NULL);
+    if (!r || count != sizeof *result)
+        ERR("service protocol error - failed to read pipe "
+            "r = %d  count = %ld/%d!\n", r, count, sizeof *result);
+    return r;
 }
 
 /******************************************************************************
- * start_new_service
- *
- * helper function for service control dispatcher
+ * service_accepts_control
  */
-static struct service_thread_data*
-start_new_service( LPSERVICE_MAIN_FUNCTIONW service_main, BOOL ascii )
+static BOOL service_accepts_control(service_data *service, DWORD dwControl)
 {
-    struct service_thread_data *thread_data;
-    unsigned int i;
-    WCHAR object_name[ MAX_PATH ];
-
-    thread_data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct service_thread_data) );
-    if( NULL == thread_data )
-        return NULL;
+    DWORD a = service->status.dwControlsAccepted;
 
-    if( ! read_scm_lock_data( thread_data->service_name ) )
+    switch (dwControl)
     {
-        /* FIXME: Instead of exiting we allow
-           service to be executed as ordinary program.
-           This behaviour was specially introduced in the patch
-           submitted against revision 1.45 and so preserved here.
-         */
-        FIXME("should fail with ERROR_FAILED_SERVICE_CONTROLLER_CONNECT\n");
-        service_main( 0, NULL );
-        HeapFree( GetProcessHeap(), 0, thread_data );
-        return NULL;
+    case SERVICE_CONTROL_INTERROGATE:
+        return TRUE;
+    case SERVICE_CONTROL_STOP:
+        if (a&SERVICE_ACCEPT_STOP)
+            return TRUE;
+        break;
+    case SERVICE_CONTROL_SHUTDOWN:
+        if (a&SERVICE_ACCEPT_SHUTDOWN)
+            return TRUE;
+        break;
+    case SERVICE_CONTROL_PAUSE:
+    case SERVICE_CONTROL_CONTINUE:
+        if (a&SERVICE_ACCEPT_PAUSE_CONTINUE)
+            return TRUE;
+        break;
+    case SERVICE_CONTROL_PARAMCHANGE:
+        if (a&SERVICE_ACCEPT_PARAMCHANGE)
+            return TRUE;
+        break;
+    case SERVICE_CONTROL_NETBINDADD:
+    case SERVICE_CONTROL_NETBINDREMOVE:
+    case SERVICE_CONTROL_NETBINDENABLE:
+    case SERVICE_CONTROL_NETBINDDISABLE:
+        if (a&SERVICE_ACCEPT_NETBINDCHANGE)
+            return TRUE;
     }
-
-    thread_data->seb = open_seb_shmem( thread_data->service_name, &thread_data->hServiceShmem );
-    if( NULL == thread_data->seb )
-        goto error;
-
-    thread_data->argv = build_arg_vectors( thread_data->seb );
-    if( NULL == thread_data->argv )
-        goto error;
-
-    thread_data->argv[0] = thread_data->service_name;
-    thread_data->argc = thread_data->seb->argc + 1;
-
-    if( ascii )
+    if (1) /* (!service->handlerex) */
+        return FALSE;
+    switch (dwControl)
     {
-        /* Convert the Unicode arg vectors back to ASCII;
-         * but we'll need unicode service name (argv[0]) for object names */
-        WideCharToMultiByte( CP_ACP, 0, thread_data->argv[0], -1,
-                             thread_data->service_nameA, MAX_SERVICE_NAME, NULL, NULL );
-        thread_data->argv[0] = (LPWSTR) thread_data->service_nameA;
-
-        for(i=1; i<thread_data->argc; i++)
-        {
-            LPWSTR src = thread_data->argv[i];
-            int len = WideCharToMultiByte( CP_ACP, 0, src, -1, NULL, 0, NULL, NULL );
-            LPSTR dest = HeapAlloc( GetProcessHeap(), 0, len );
-            if( NULL == dest )
-                goto error;
-            WideCharToMultiByte( CP_ACP, 0, src, -1, dest, len, NULL, NULL );
-            /* copy converted string back  */
-            memcpy( src, dest, len );
-            HeapFree( GetProcessHeap(), 0, dest );
-        }
+    case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
+        if (a&SERVICE_ACCEPT_HARDWAREPROFILECHANGE)
+            return TRUE;
+        break;
+    case SERVICE_CONTROL_POWEREVENT:
+        if (a&SERVICE_ACCEPT_POWEREVENT)
+            return TRUE;
+        break;
+    case SERVICE_CONTROL_SESSIONCHANGE:
+        if (a&SERVICE_ACCEPT_SESSIONCHANGE)
+            return TRUE;
+        break;
     }
+    return FALSE;
+}
 
-    /* init status according to docs for StartService */
-    thread_data->seb->status.dwCurrentState = SERVICE_START_PENDING;
-    thread_data->seb->status.dwControlsAccepted = 0;
-    thread_data->seb->status.dwCheckPoint = 0;
-    thread_data->seb->status.dwWaitHint = 2000;
-
-    /* create service mutex; mutex is initially owned */
-    snprintfW( object_name, MAX_PATH, szServiceMutexNameFmtW, thread_data->service_name );
-    thread_data->mutex = CreateMutexW( NULL, TRUE, object_name );
-    if( NULL == thread_data->mutex )
-        goto error;
+/******************************************************************************
+ * service_handle_control
+ */
+static BOOL service_handle_control(HANDLE pipe, service_data *service,
+                                   DWORD dwControl)
+{
+    DWORD count, ret = ERROR_INVALID_SERVICE_CONTROL;
 
-    if( ERROR_ALREADY_EXISTS == GetLastError() )
+    TRACE("received control %ld\n", dwControl);
+    
+    if (service_accepts_control(service, dwControl) && service->handler)
     {
-        SetLastError( ERROR_SERVICE_ALREADY_RUNNING );
-        goto error;
+        service->handler(dwControl);
+        ret = ERROR_SUCCESS;
     }
+    return WriteFile(pipe, &ret, sizeof ret, &count, NULL);
+}
 
-    /* create service event */
-    snprintfW( object_name, MAX_PATH, szServiceAckEventNameFmtW, thread_data->service_name );
-    thread_data->ack_event = CreateEventW( NULL, FALSE, FALSE, object_name );
-    if( NULL == thread_data->ack_event )
-        goto error;
+/******************************************************************************
+ * service_reap_thread
+ */
+static DWORD service_reap_thread(service_data *service)
+{
+    DWORD exitcode = 0;
 
-    if( ERROR_ALREADY_EXISTS == GetLastError() )
+    if (!service->thread)
+        return 0;
+    GetExitCodeThread(service->thread, &exitcode);
+    if (exitcode!=STILL_ACTIVE)
     {
-        SetLastError( ERROR_SERVICE_ALREADY_RUNNING );
-        goto error;
+        CloseHandle(service->thread);
+        service->thread = 0;
     }
-
-    /* create service thread in suspended state
-     * to avoid race while caller handles return value */
-    thread_data->service_main = service_main;
-    thread_data->thread_handle = CreateThread( NULL, 0, service_thread,
-                                               thread_data, CREATE_SUSPENDED, NULL );
-    if( thread_data->thread_handle )
-        return thread_data;
-
-error:
-    dispose_service_thread_data( thread_data );
-    return FALSE;
+    return exitcode;
 }
 
 /******************************************************************************
- * service_ctrl_dispatcher
+ * service_control_dispatcher
  */
-static BOOL service_ctrl_dispatcher( LPSERVICE_TABLE_ENTRYW servent, BOOL ascii )
+static DWORD WINAPI service_control_dispatcher(LPVOID arg)
 {
-    WCHAR object_name[ MAX_PATH ];
-    HANDLE wait;
-
-    /* FIXME: if shared service, find entry by service name */
+    service_data *service = arg;
+    LPWSTR name;
+    HANDLE pipe, event;
 
-    /* FIXME: move this into dispatcher loop */
-    service = start_new_service( servent->lpServiceProc, ascii );
-    if( NULL == service )
-        return FALSE;
+    TRACE("%p %s\n", service, debugstr_w(service->name));
 
-    ResumeThread( service->thread_handle );
+    /* create a pipe to talk to the rest of the world with */
+    name = service_get_pipe_name(service->name);
+    pipe = CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX,
+                  PIPE_TYPE_BYTE|PIPE_WAIT, 1, 256, 256, 10000, NULL );
+    SERV_free(name);
 
-    /* create dispatcher event object */
-    /* FIXME: object_name should be based on executable image path because
-     * this object is common for all services in the process */
-    /* But what if own and shared services have the same executable? */
-    snprintfW( object_name, MAX_PATH, szServiceDispEventNameFmtW, service->service_name );
-    dispatcher_event = CreateEventW( NULL, FALSE, FALSE, object_name );
-    if( NULL == dispatcher_event )
-    {
-        dispose_service_thread_data( service );
-        return FALSE;
-    }
+    /* let the process who started us know we've tried to create a pipe */
+    event = service_get_event_handle(service->name);
+    SetEvent(event);
+    CloseHandle(event);
 
-    if( ERROR_ALREADY_EXISTS == GetLastError() )
+    if (pipe==INVALID_HANDLE_VALUE)
     {
-        SetLastError( ERROR_SERVICE_ALREADY_RUNNING );
-        CloseHandle( dispatcher_event );
-        return FALSE;
-    }
-
-    /* ready to accept control requests */
-    ReleaseMutex( service->mutex );
-
-    /* signal for StartService */
-    wait = OpenSemaphoreW( SEMAPHORE_MODIFY_STATE, FALSE, szWaitServiceStartW );
-    if( wait )
-    {
-        ReleaseSemaphore( wait, 1, NULL );
-        CloseHandle( wait );
+        ERR("failed to create pipe, error = %ld\n", GetLastError());
+        return 0;
     }
 
     /* dispatcher loop */
-    for(;;)
+    while (1)
     {
-        DWORD ret;
+        BOOL r;
+        DWORD count, req[2] = {0,0};
 
-        WaitForSingleObject( dispatcher_event, INFINITE );
+        r = ConnectNamedPipe(pipe, NULL);
+        if (!r && GetLastError() != ERROR_PIPE_CONNECTED)
+        {
+            ERR("pipe connect failed\n");
+            break;
+        }
 
-        /* at first, look for terminated service thread
-         * FIXME: threads, if shared service */
-        if( !GetExitCodeThread( service->thread_handle, &ret ) )
-            ERR("Couldn't get thread exit code\n");
-        else if( ret != STILL_ACTIVE )
+        r = ReadFile( pipe, &req, sizeof req, &count, NULL );
+        if (!r || count!=sizeof req)
         {
-            CloseHandle( service->thread_handle );
-            dispose_service_thread_data( service );
+            ERR("pipe read failed\n");
             break;
         }
 
-        /* look for control requests */
-        if( service->seb->control_code )
+        service_reap_thread(service);
+
+        /* handle the request */
+        switch (req[0])
         {
-            if( NULL == service->ctrl_handler )
-                service->seb->dispatcher_error = ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
-            else
-            {
-                service->ctrl_handler( service->seb->control_code );
-                service->seb->dispatcher_error = 0;
-            }
-            service->seb->control_code = 0;
-            SetEvent( service->ack_event );
+        case WINESERV_STARTINFO:
+            service_handle_start(pipe, service, req[1]);
+            break;
+        case WINESERV_GETSTATUS:
+            service_handle_get_status(pipe, service);
+            break;
+        case WINESERV_SENDCONTROL:
+            service_handle_control(pipe, service, req[1]);
+            break;
+        default:
+            ERR("received invalid command %ld length %ld\n", req[0], req[1]);
         }
 
-        /* FIXME: if shared service, check SCM lock object;
-         * if exists, a new service should be started */
+        FlushFileBuffers(pipe);
+        DisconnectNamedPipe(pipe);
     }
 
-    CloseHandle( dispatcher_event );
-    return TRUE;
+    CloseHandle(pipe);
+    return 1;
 }
 
 /******************************************************************************
- * StartServiceCtrlDispatcherA [ADVAPI32.@]
+ * service_run_threads
  */
-BOOL WINAPI
-StartServiceCtrlDispatcherA( LPSERVICE_TABLE_ENTRYA servent )
+static BOOL service_run_threads(void)
 {
-    int count, i;
-    LPSERVICE_TABLE_ENTRYW ServiceTableW;
-    BOOL ret;
+    service_data *service;
+    DWORD count = 0, n = 0;
+    HANDLE *handles;
 
-    TRACE("(%p)\n", servent);
+    EnterCriticalSection( &service_cs );
 
-    /* convert service table to unicode */
-    for( count = 0; servent[ count ].lpServiceName; )
+    /* count how many services there are */
+    for (service = service_list; service; service = service->next)
         count++;
-    ServiceTableW = HeapAlloc( GetProcessHeap(), 0, (count + 1) * sizeof(SERVICE_TABLE_ENTRYW) );
-    if( NULL == ServiceTableW )
-        return FALSE;
 
-    for( i = 0; i < count; i++ )
+    TRACE("starting %ld pipe listener threads\n", count);
+
+    handles = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE)*count);
+
+    for (n=0, service = service_list; service; service = service->next, n++)
+        handles[n] = CreateThread( NULL, 0, service_control_dispatcher,
+                                   service, 0, NULL );
+    assert(n==count);
+
+    LeaveCriticalSection( &service_cs );
+
+    /* wait for all the threads to pack up and exit */
+    WaitForMultipleObjectsEx(count, handles, TRUE, INFINITE, FALSE);
+
+    HeapFree(GetProcessHeap(), 0, handles);
+
+    return TRUE;
+}
+
+/******************************************************************************
+ * StartServiceCtrlDispatcherA [ADVAPI32.@]
+ *
+ *  Connects a process containing one or more services to the service control
+ * manager.
+ *
+ * PARAMS
+ *   servent [I]  A list of the service names and service procedures
+ */
+BOOL WINAPI StartServiceCtrlDispatcherA( LPSERVICE_TABLE_ENTRYA servent )
+{
+    service_data *info;
+    DWORD sz, len;
+    BOOL ret = TRUE;
+
+    TRACE("%p\n", servent);
+
+    EnterCriticalSection( &service_cs );
+    while (servent->lpServiceName)
     {
-        ServiceTableW[ i ].lpServiceName = SERV_dup( servent[ i ].lpServiceName );
-        ServiceTableW[ i ].lpServiceProc = (LPSERVICE_MAIN_FUNCTIONW) servent[ i ].lpServiceProc;
+        LPSTR name = servent->lpServiceName;
+
+        len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0);
+        sz = len*sizeof(WCHAR) + sizeof *info;
+        info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz );
+        MultiByteToWideChar(CP_ACP, 0, name, -1, info->name, len);
+        info->proc.a = servent->lpServiceProc;
+        info->unicode = FALSE;
+        
+        /* insert into the list */
+        info->next = service_list;
+        service_list = info;
+
+        servent++;
     }
-    ServiceTableW[ count ].lpServiceName = NULL;
-    ServiceTableW[ count ].lpServiceProc = NULL;
+    LeaveCriticalSection( &service_cs );
 
-    /* start dispatcher */
-    ret = service_ctrl_dispatcher( ServiceTableW, TRUE );
+    service_run_threads();
 
-    /* free service table */
-    for( i = 0; i < count; i++ )
-        SERV_free( ServiceTableW[ i ].lpServiceName );
-    HeapFree( GetProcessHeap(), 0, ServiceTableW );
     return ret;
 }
 
 /******************************************************************************
  * StartServiceCtrlDispatcherW [ADVAPI32.@]
  *
+ *  Connects a process containing one or more services to the service control
+ * manager.
+ *
  * PARAMS
- *   servent []
+ *   servent [I]  A list of the service names and service procedures
  */
-BOOL WINAPI
-StartServiceCtrlDispatcherW( LPSERVICE_TABLE_ENTRYW servent )
+BOOL WINAPI StartServiceCtrlDispatcherW( LPSERVICE_TABLE_ENTRYW servent )
 {
-    TRACE("(%p)\n", servent);
-    return service_ctrl_dispatcher( servent, FALSE );
+    service_data *info;
+    DWORD sz, len;
+    BOOL ret = TRUE;
+
+    TRACE("%p\n", servent);
+
+    EnterCriticalSection( &service_cs );
+    while (servent->lpServiceName)
+    {
+        LPWSTR name = servent->lpServiceName;
+
+        len = strlenW(name);
+        sz = len*sizeof(WCHAR) + sizeof *info;
+        info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sz );
+        strcpyW(info->name, name);
+        info->proc.w = servent->lpServiceProc;
+        info->unicode = TRUE;
+        
+        /* insert into the list */
+        info->next = service_list;
+        service_list = info;
+
+        servent++;
+    }
+    LeaveCriticalSection( &service_cs );
+
+    service_run_threads();
+
+    return ret;
 }
 
 /******************************************************************************
@@ -572,8 +745,7 @@ SC_LOCK WINAPI LockServiceDatabase (SC_H
 
     TRACE("%p\n",hSCManager);
 
-    ret = CreateFileMappingW( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
-                              0, MAX_SERVICE_NAME * sizeof(WCHAR), szSCMLock );
+    ret = CreateSemaphoreW( NULL, 1, 1, szSCMLock );
     if( ret && GetLastError() == ERROR_ALREADY_EXISTS )
     {
         CloseHandle( ret );
@@ -593,15 +765,14 @@ BOOL WINAPI UnlockServiceDatabase (SC_LO
 {
     TRACE("%p\n",ScLock);
 
-    return CloseHandle( (HANDLE) ScLock );
+    return CloseHandle( ScLock );
 }
 
 /******************************************************************************
  * RegisterServiceCtrlHandlerA [ADVAPI32.@]
  */
 SERVICE_STATUS_HANDLE WINAPI
-RegisterServiceCtrlHandlerA( LPCSTR lpServiceName,
-                             LPHANDLER_FUNCTION lpfHandler )
+RegisterServiceCtrlHandlerA( LPCSTR lpServiceName, LPHANDLER_FUNCTION lpfHandler )
 {
     LPWSTR lpServiceNameW;
     SERVICE_STATUS_HANDLE ret;
@@ -619,14 +790,20 @@ RegisterServiceCtrlHandlerA( LPCSTR lpSe
  *   lpServiceName []
  *   lpfHandler    []
  */
-SERVICE_STATUS_HANDLE WINAPI
-RegisterServiceCtrlHandlerW( LPCWSTR lpServiceName,
+SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerW( LPCWSTR lpServiceName,
                              LPHANDLER_FUNCTION lpfHandler )
 {
-    /* FIXME: find service thread data by service name */
+    service_data *service;
+
+    EnterCriticalSection( &service_cs );
+    for(service = service_list; service; service = service->next)
+        if(!strcmpW(lpServiceName, service->name))
+            break;
+    if (service)
+        service->handler = lpfHandler;
+    LeaveCriticalSection( &service_cs );
 
-    service->ctrl_handler = lpfHandler;
-    return 0xcacacafe;
+    return (SERVICE_STATUS_HANDLE)service;
 }
 
 /******************************************************************************
@@ -639,28 +816,32 @@ RegisterServiceCtrlHandlerW( LPCWSTR lpS
 BOOL WINAPI
 SetServiceStatus( SERVICE_STATUS_HANDLE hService, LPSERVICE_STATUS lpStatus )
 {
-    DWORD r;
+    service_data *service;
+    BOOL r = TRUE;
 
-    TRACE("\tType:%lx\n",lpStatus->dwServiceType);
-    TRACE("\tState:%lx\n",lpStatus->dwCurrentState);
-    TRACE("\tControlAccepted:%lx\n",lpStatus->dwControlsAccepted);
-    TRACE("\tExitCode:%lx\n",lpStatus->dwWin32ExitCode);
-    TRACE("\tServiceExitCode:%lx\n",lpStatus->dwServiceSpecificExitCode);
-    TRACE("\tCheckPoint:%lx\n",lpStatus->dwCheckPoint);
-    TRACE("\tWaitHint:%lx\n",lpStatus->dwWaitHint);
-
-    /* FIXME: find service thread data by status handle */
-
-    /* acquire mutex; note that mutex may already be owned
-     * when service handles control request
-     */
-    r = WaitForSingleObject( service->mutex, 0 );
-    memcpy( &service->seb->status, lpStatus, sizeof(SERVICE_STATUS) );
-    if( WAIT_OBJECT_0 == r || WAIT_ABANDONED == r )
-        ReleaseMutex( service->mutex );
-    return TRUE;
+    TRACE("%lx %lx %lx %lx %lx %lx %lx %lx\n", hService,
+          lpStatus->dwServiceType, lpStatus->dwCurrentState,
+          lpStatus->dwControlsAccepted, lpStatus->dwWin32ExitCode,
+          lpStatus->dwServiceSpecificExitCode, lpStatus->dwCheckPoint,
+          lpStatus->dwWaitHint);
+
+    EnterCriticalSection( &service_cs );
+    for (service = service_list; service; service = service->next)
+        if(service == (service_data*)hService)
+            break;
+    if (service)
+    {
+        memcpy( &service->status, lpStatus, sizeof(SERVICE_STATUS) );
+        TRACE("Set service status to %ld\n",service->status.dwCurrentState);
+    }
+    else
+        r = FALSE;
+    LeaveCriticalSection( &service_cs );
+
+    return r;
 }
 
+
 /******************************************************************************
  * OpenSCManagerA [ADVAPI32.@]
  *
@@ -768,12 +949,10 @@ BOOL WINAPI ControlService( SC_HANDLE hS
                             LPSERVICE_STATUS lpServiceStatus )
 {
     struct sc_service *hsvc;
-    WCHAR object_name[ MAX_PATH ];
-    HANDLE mutex = NULL, shmem = NULL;
-    HANDLE disp_event = NULL, ack_event = NULL;
-    struct SEB *seb = NULL;
-    DWORD  r;
-    BOOL ret = FALSE, mutex_owned = FALSE;
+    BOOL ret = FALSE;
+    HANDLE handle;
+
+    TRACE("%p %ld %p\n", hService, dwControl, lpServiceStatus);
 
     hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE);
     if (!hsvc)
@@ -782,79 +961,41 @@ BOOL WINAPI ControlService( SC_HANDLE hS
         return FALSE;
     }
 
-    TRACE("%p(%s) %ld %p\n", hService, debugstr_w(hsvc->name),
-          dwControl, lpServiceStatus);
-
-    /* open and hold mutex */
-    snprintfW( object_name, MAX_PATH, szServiceMutexNameFmtW, hsvc->name );
-    mutex = OpenMutexW( MUTEX_ALL_ACCESS, FALSE, object_name );
-    if( NULL == mutex )
+    ret = QueryServiceStatus(hService, lpServiceStatus);
+    if (ret)
     {
-        SetLastError( ERROR_SERVICE_NOT_ACTIVE );
+        ERR("failed to query service status\n");
+        SetLastError(ERROR_SERVICE_NOT_ACTIVE);
         return FALSE;
     }
 
-    r = WaitForSingleObject( mutex, 30000 );
-    if( WAIT_FAILED == r )
-        goto done;
-
-    if( WAIT_TIMEOUT == r )
+    switch (lpServiceStatus->dwCurrentState)
     {
-        SetLastError( ERROR_SERVICE_REQUEST_TIMEOUT );
-        goto done;
-    }
-    mutex_owned = TRUE;
-
-    /* open event objects */
-    snprintfW( object_name, MAX_PATH, szServiceDispEventNameFmtW, hsvc->name );
-    disp_event = OpenEventW( EVENT_ALL_ACCESS, FALSE, object_name );
-    if( NULL == disp_event )
-        goto done;
-
-    snprintfW( object_name, MAX_PATH, szServiceAckEventNameFmtW, hsvc->name );
-    ack_event = OpenEventW( EVENT_ALL_ACCESS, FALSE, object_name );
-    if( NULL == ack_event )
-        goto done;
-
-    /* get service environment block */
-    seb = open_seb_shmem( hsvc->name, &shmem );
-    if( NULL == seb )
-        goto done;
-
-    /* send request */
-    /* FIXME: check dwControl against controls accepted */
-    seb->control_code = dwControl;
-    SetEvent( disp_event );
-
-    /* wait for acknowledgement */
-    r = WaitForSingleObject( ack_event, 30000 );
-    if( WAIT_FAILED == r )
-        goto done;
-
-    if( WAIT_TIMEOUT == r )
-    {
-        SetLastError( ERROR_SERVICE_REQUEST_TIMEOUT );
-        goto done;
+    case SERVICE_STOPPED:
+        SetLastError(ERROR_SERVICE_NOT_ACTIVE);
+        return FALSE;
+    case SERVICE_START_PENDING:
+        if (dwControl==SERVICE_CONTROL_STOP)
+            break;
+        /* fall thru */
+    case SERVICE_STOP_PENDING:
+        SetLastError(ERROR_SERVICE_CANNOT_ACCEPT_CTRL);
+        return FALSE;
     }
 
-    if( seb->dispatcher_error )
+    handle = service_open_pipe(hsvc->name);
+    if (handle!=INVALID_HANDLE_VALUE)
     {
-        SetLastError( seb->dispatcher_error );
-        goto done;
+        DWORD result = ERROR_SUCCESS;
+        ret = service_send_control(handle, dwControl, &result);
+        CloseHandle(handle);
+        if (result!=ERROR_SUCCESS)
+        {
+            SetLastError(result);
+            ret = FALSE;
+        }
     }
 
-    /* get status */
-    if( lpServiceStatus )
-        memcpy( lpServiceStatus, &seb->status, sizeof(SERVICE_STATUS) );
-    ret = TRUE;
-
-done:
-    if( seb ) UnmapViewOfFile( seb );
-    if( shmem ) CloseHandle( shmem );
-    if( ack_event ) CloseHandle( ack_event );
-    if( disp_event ) CloseHandle( disp_event );
-    if( mutex_owned ) ReleaseMutex( mutex );
-    if( mutex ) CloseHandle( mutex );
     return ret;
 }
 
@@ -1209,185 +1350,160 @@ BOOL WINAPI DeleteService( SC_HANDLE hSe
  *   Success: TRUE.
  *   Failure: FALSE
  */
-BOOL WINAPI
-StartServiceA( SC_HANDLE hService, DWORD dwNumServiceArgs,
-                 LPCSTR *lpServiceArgVectors )
+BOOL WINAPI StartServiceA( SC_HANDLE hService, DWORD dwNumServiceArgs,
+                           LPCSTR *lpServiceArgVectors )
 {
     LPWSTR *lpwstr=NULL;
     unsigned int i;
+    BOOL r;
 
     TRACE("(%p,%ld,%p)\n",hService,dwNumServiceArgs,lpServiceArgVectors);
 
-    if(dwNumServiceArgs)
+    if (dwNumServiceArgs)
         lpwstr = HeapAlloc( GetProcessHeap(), 0,
                                    dwNumServiceArgs*sizeof(LPWSTR) );
 
     for(i=0; i<dwNumServiceArgs; i++)
         lpwstr[i]=SERV_dup(lpServiceArgVectors[i]);
 
-    StartServiceW(hService, dwNumServiceArgs, (LPCWSTR *)lpwstr);
+    r = StartServiceW(hService, dwNumServiceArgs, (LPCWSTR *)lpwstr);
 
-    if(dwNumServiceArgs)
+    if (dwNumServiceArgs)
     {
         for(i=0; i<dwNumServiceArgs; i++)
             SERV_free(lpwstr[i]);
         HeapFree(GetProcessHeap(), 0, lpwstr);
     }
 
-    return TRUE;
+    return r;
 }
 
-
 /******************************************************************************
- * StartServiceW [ADVAPI32.@]
- * 
- * See StartServiceA.
+ * service_start_process    [INTERNAL]
  */
-BOOL WINAPI
-StartServiceW( SC_HANDLE hService, DWORD dwNumServiceArgs,
-                 LPCWSTR *lpServiceArgVectors )
+static DWORD service_start_process(struct sc_service *hsvc)
 {
-    static const WCHAR  _ImagePathW[]  = {'I','m','a','g','e','P','a','t','h',0};
-                                                
-    struct sc_service *hsvc;
-    WCHAR path[MAX_PATH],str[MAX_PATH];
-    DWORD type,size;
-    DWORD i;
-    long r;
-    HANDLE hLock;
-    HANDLE hServiceShmem = NULL;
-    HANDLE wait = NULL;
-    LPWSTR shmem_lock = NULL;
-    struct SEB *seb = NULL;
-    LPWSTR argptr;
-    PROCESS_INFORMATION procinfo;
-    STARTUPINFOW startupinfo;
-    BOOL ret = FALSE;
-
-    TRACE("%p %ld %p\n",hService,dwNumServiceArgs,
-          lpServiceArgVectors);
-
-    hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE);
-    if (!hsvc)
-    {
-        SetLastError( ERROR_INVALID_HANDLE );
+    static const WCHAR _ImagePathW[] = {'I','m','a','g','e','P','a','t','h',0};
+    PROCESS_INFORMATION pi;
+    STARTUPINFOW si;
+    LPWSTR path = NULL, str;
+    DWORD type, size, ret;
+    HANDLE handles[2];
+    BOOL r;
+
+    /* read the executable path from memory */
+    size = 0;
+    ret = RegQueryValueExW(hsvc->hkey, _ImagePathW, NULL, &type, NULL, &size);
+    if (ret!=ERROR_SUCCESS)
         return FALSE;
+    str = HeapAlloc(GetProcessHeap(),0,size);
+    ret = RegQueryValueExW(hsvc->hkey, _ImagePathW, NULL, &type, (LPBYTE)str, &size);
+    if (ret==ERROR_SUCCESS)
+    {
+        size = ExpandEnvironmentStringsW(str,NULL,0);
+        path = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR));
+        ExpandEnvironmentStringsW(str,path,size);
     }
-
-    size = sizeof(str);
-    r = RegQueryValueExW(hsvc->hkey, _ImagePathW, NULL, &type, (LPVOID)str, &size);
-    if (r!=ERROR_SUCCESS)
-        return FALSE;
-    ExpandEnvironmentStringsW(str,path,sizeof(path));
-
-    TRACE("Starting service %s\n", debugstr_w(path) );
-
-    hLock = LockServiceDatabase( (SC_HANDLE) &hsvc->scm->hdr );
-    if( NULL == hLock )
+    HeapFree(GetProcessHeap(),0,str);
+    if (!path)
         return FALSE;
 
-    /*
-     * FIXME: start dependent services
-     */
+    /* wait for the process to start and set an event or terminate */
+    handles[0] = service_get_event_handle( hsvc->name );
+    ZeroMemory(&si, sizeof(STARTUPINFOW));
+    si.cb = sizeof(STARTUPINFOW);
+    r = CreateProcessW(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+    if (r)
+    {
+        handles[1] = pi.hProcess;
+        ret = WaitForMultipleObjectsEx(2, handles, FALSE, 30000, FALSE);
+        if(ret != WAIT_OBJECT_0)
+        {
+            SetLastError(ERROR_IO_PENDING);
+            r = FALSE;
+        }
 
-    /* pass argv[0] (service name) to the service via global SCM lock object */
-    shmem_lock = MapViewOfFile( hLock, FILE_MAP_ALL_ACCESS,
-                                0, 0, MAX_SERVICE_NAME * sizeof(WCHAR) );
-    if( NULL == shmem_lock )
-    {
-        ERR("Couldn't map shared memory\n");
-        goto done;
+        CloseHandle( pi.hThread );
+        CloseHandle( pi.hProcess );
     }
-    strcpyW( shmem_lock, hsvc->name );
-
-    /* create service environment block */
-    size = sizeof(struct SEB);
-    for( i = 0; i < dwNumServiceArgs; i++ )
-        size += sizeof(WCHAR) * (1 + strlenW( lpServiceArgVectors[ i ] ));
+    CloseHandle( handles[0] );
+    HeapFree(GetProcessHeap(),0,path);
+    return r;
+}
 
-    snprintfW( str, MAX_PATH, szServiceShmemNameFmtW, hsvc->name );
-    hServiceShmem = CreateFileMappingW( INVALID_HANDLE_VALUE,
-                                        NULL, PAGE_READWRITE, 0, size, str );
-    if( NULL == hServiceShmem )
-    {
-        ERR("Couldn't create shared memory object\n");
-        goto done;
-    }
-    if( GetLastError() == ERROR_ALREADY_EXISTS )
-    {
-        SetLastError( ERROR_SERVICE_ALREADY_RUNNING );
-        goto done;
-    }
-    seb = MapViewOfFile( hServiceShmem, FILE_MAP_ALL_ACCESS, 0, 0, 0 );
-    if( NULL == seb )
-    {
-        ERR("Couldn't map shared memory\n");
-        goto done;
-    }
+static BOOL service_wait_for_startup(SC_HANDLE hService)
+{
+    DWORD i;
+    SERVICE_STATUS status;
+    BOOL r = FALSE;
 
-    /* copy service args to SEB */
-    seb->argc = dwNumServiceArgs;
-    argptr = (LPWSTR) &seb[1];
-    for( i = 0; i < dwNumServiceArgs; i++ )
-    {
-        strcpyW( argptr, lpServiceArgVectors[ i ] );
-        argptr += 1 + strlenW( argptr );
-    }
+    TRACE("%p\n", hService);
 
-    wait = CreateSemaphoreW(NULL,0,1,szWaitServiceStartW);
-    if (!wait)
+    for (i=0; i<30; i++)
     {
-        ERR("Couldn't create wait semaphore\n");
-        goto done;
+        status.dwCurrentState = 0;
+        r = QueryServiceStatus(hService, &status);
+        if (!r)
+            break;
+        if (status.dwCurrentState == SERVICE_RUNNING)
+        {
+            TRACE("Service started successfully\n");
+            break;
+        }
+        r = FALSE;
+        Sleep(1000);
     }
+    return r;
+}
 
-    ZeroMemory(&startupinfo,sizeof(STARTUPINFOW));
-    startupinfo.cb = sizeof(STARTUPINFOW);
+/******************************************************************************
+ * StartServiceW [ADVAPI32.@]
+ * 
+ * See StartServiceA.
+ */
+BOOL WINAPI StartServiceW(SC_HANDLE hService, DWORD dwNumServiceArgs,
+                          LPCWSTR *lpServiceArgVectors)
+{
+    struct sc_service *hsvc;
+    BOOL r = FALSE;
+    SC_LOCK hLock;
+    HANDLE handle = INVALID_HANDLE_VALUE;
 
-    r = CreateProcessW(NULL,
-                   path,
-                   NULL,  /* process security attribs */
-                   NULL,  /* thread security attribs */
-                   FALSE, /* inherit handles */
-                   0,     /* creation flags */
-                   NULL,  /* environment */
-                   NULL,  /* current directory */
-                   &startupinfo,  /* startup info */
-                   &procinfo); /* process info */
+    TRACE("%p %ld %p\n", hService, dwNumServiceArgs, lpServiceArgVectors);
 
-    if(r == FALSE)
+    hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE);
+    if (!hsvc)
     {
-        ERR("Couldn't start process\n");
-        goto done;
+        SetLastError(ERROR_INVALID_HANDLE);
+        return r;
     }
-    CloseHandle( procinfo.hThread );
 
-    /* docs for StartServiceCtrlDispatcher say this should be 30 sec */
-    r = WaitForSingleObject(wait,30000);
-    if( WAIT_FAILED == r )
+    hLock = LockServiceDatabase(hsvc->scm);
+    if (!hLock)
+        return r;
+
+    handle = service_open_pipe(hsvc->name);
+    if (handle==INVALID_HANDLE_VALUE)
     {
-        CloseHandle( procinfo.hProcess );
-        goto done;
+        /* start the service process */
+        if (service_start_process(hsvc))
+            handle = service_open_pipe(hsvc->name);
     }
-    if( WAIT_TIMEOUT == r )
+
+    if (handle != INVALID_HANDLE_VALUE)
     {
-        TerminateProcess( procinfo.hProcess, 1 );
-        CloseHandle( procinfo.hProcess );
-        SetLastError( ERROR_SERVICE_REQUEST_TIMEOUT );
-        goto done;
+        service_send_start_message(handle, lpServiceArgVectors, dwNumServiceArgs);
+        CloseHandle(handle);
+        r = TRUE;
     }
 
-    /* allright */
-    CloseHandle( procinfo.hProcess );
-    ret = TRUE;
-
-done:
-    if( wait ) CloseHandle( wait );
-    if( seb != NULL ) UnmapViewOfFile( seb );
-    if( hServiceShmem != NULL ) CloseHandle( hServiceShmem );
-    if( shmem_lock != NULL ) UnmapViewOfFile( shmem_lock );
     UnlockServiceDatabase( hLock );
-    return ret;
+
+    TRACE("returning %d\n", r);
+
+    service_wait_for_startup(hService);
+
+    return r;
 }
 
 /******************************************************************************
@@ -1398,16 +1514,15 @@ done:
  *   lpservicestatus []
  *
  */
-BOOL WINAPI
-QueryServiceStatus( SC_HANDLE hService, LPSERVICE_STATUS lpservicestatus )
+BOOL WINAPI QueryServiceStatus(SC_HANDLE hService,
+                               LPSERVICE_STATUS lpservicestatus)
 {
     struct sc_service *hsvc;
+    DWORD size, type, val;
+    HANDLE pipe;
     LONG r;
-    DWORD type, val, size;
-    WCHAR object_name[ MAX_PATH ];
-    HANDLE mutex, shmem = NULL;
-    struct SEB *seb = NULL;
-    BOOL ret = FALSE, mutex_owned = FALSE;
+
+    TRACE("%p %p\n", hService, lpservicestatus);
 
     hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE);
     if (!hsvc)
@@ -1416,54 +1531,27 @@ QueryServiceStatus( SC_HANDLE hService, 
         return FALSE;
     }
 
-    /* try to open service mutex */
-    snprintfW( object_name, MAX_PATH, szServiceMutexNameFmtW, hsvc->name );
-    mutex = OpenMutexW( MUTEX_ALL_ACCESS, FALSE, object_name );
-    if( NULL == mutex )
-        goto stopped;
-
-    /* hold mutex */
-    r = WaitForSingleObject( mutex, 30000 );
-    if( WAIT_FAILED == r )
-        goto done;
-
-    if( WAIT_TIMEOUT == r )
-    {
-        SetLastError( ERROR_SERVICE_REQUEST_TIMEOUT );
-        goto done;
-    }
-    mutex_owned = TRUE;
-
-    /* get service environment block */
-    seb = open_seb_shmem( hsvc->name, &shmem );
-    if( NULL == seb )
-        goto done;
- 
-    /* get status */
-    memcpy( lpservicestatus, &seb->status, sizeof(SERVICE_STATUS) );
-    ret = TRUE;
-
-done:
-    if( seb ) UnmapViewOfFile( seb );
-    if( shmem ) CloseHandle( shmem );
-    if( mutex_owned ) ReleaseMutex( mutex );
-    CloseHandle( mutex );
-    return ret;
- 
-stopped:
-    /* service stopped */
+    pipe = service_open_pipe(hsvc->name);
+    if (pipe != INVALID_HANDLE_VALUE)
+    {
+        r = service_get_status(pipe, lpservicestatus);
+        CloseHandle(pipe);
+        if (r)
+            return TRUE;
+    }
+
+    TRACE("Failed to read service status\n");
+
     /* read the service type from the registry */
     size = sizeof(val);
     r = RegQueryValueExA(hsvc->hkey, "Type", NULL, &type, (LPBYTE)&val, &size);
-    if(type!=REG_DWORD)
-    {
-        ERR("invalid Type\n");
-        return FALSE;
-    }
+    if(r!=ERROR_SUCCESS || type!=REG_DWORD)
+        val = 0;
+
     lpservicestatus->dwServiceType = val;
-    lpservicestatus->dwCurrentState            = 1;  /* stopped */
+    lpservicestatus->dwCurrentState            = SERVICE_STOPPED;  /* stopped */
     lpservicestatus->dwControlsAccepted        = 0;
-    lpservicestatus->dwWin32ExitCode           = NO_ERROR;
+    lpservicestatus->dwWin32ExitCode           = ERROR_SERVICE_NEVER_STARTED;
     lpservicestatus->dwServiceSpecificExitCode = 0;
     lpservicestatus->dwCheckPoint              = 0;
     lpservicestatus->dwWaitHint                = 0;


More information about the wine-patches mailing list