services: Send the service name in the control requests.

Only start a single dispatcher thread for all services.
diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c
index a1407dd..29c1a09 100644
--- a/dlls/advapi32/service.c
+++ b/dlls/advapi32/service.c
@@ -81,6 +81,7 @@
     LPHANDLER_FUNCTION_EX handler;
     LPVOID context;
     HANDLE thread;
+    SC_HANDLE handle;
     BOOL unicode : 1;
     union {
         LPSERVICE_MAIN_FUNCTIONA a;
@@ -356,6 +357,17 @@
     return handle;
 }
 
+static service_data *find_service_by_name( const WCHAR *name )
+{
+    unsigned int i;
+
+    if (nb_services == 1)  /* only one service (FIXME: should depend on OWN_PROCESS etc.) */
+        return services[0];
+    for (i = 0; i < nb_services; i++)
+        if (!strcmpiW( name, services[i]->name )) return services[i];
+    return NULL;
+}
+
 /******************************************************************************
  * service_thread
  *
@@ -420,57 +432,37 @@
 /******************************************************************************
  * service_handle_start
  */
-static BOOL service_handle_start(HANDLE pipe, service_data *service, DWORD count)
+static DWORD service_handle_start(service_data *service, const WCHAR *data, DWORD count)
 {
-    DWORD read = 0, result = 0;
-    LPWSTR args;
-    BOOL r;
-
-    TRACE("%p %p %d\n", pipe, service, count);
-
-    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 = %d read = %d args[n-1]=%s\n",
-            r, count, read, debugstr_wn(args, count));
-        goto end;
-    }
+    TRACE("%s argsize %u\n", debugstr_w(service->name), count);
 
     if (service->thread)
     {
         WARN("service is not stopped\n");
-        result = ERROR_SERVICE_ALREADY_RUNNING;
-        goto end;
+        return ERROR_SERVICE_ALREADY_RUNNING;
     }
 
     HeapFree(GetProcessHeap(), 0, service->args);
-    service->args = args;
-    args = NULL;
+    service->args = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR));
+    memcpy( service->args, data, count * sizeof(WCHAR) );
     service->thread = CreateThread( NULL, 0, service_thread,
                                     service, 0, NULL );
     SetEvent( service_event );  /* notify the main loop */
-
-end:
-    HeapFree(GetProcessHeap(), 0, args);
-    WriteFile( pipe, &result, sizeof result, &read, NULL );
-
-    return TRUE;
+    return 0;
 }
 
 /******************************************************************************
  * service_handle_control
  */
-static BOOL service_handle_control(HANDLE pipe, service_data *service,
-                                   DWORD dwControl)
+static DWORD service_handle_control(service_data *service, DWORD dwControl)
 {
-    DWORD count, ret = ERROR_INVALID_SERVICE_CONTROL;
+    DWORD ret = ERROR_INVALID_SERVICE_CONTROL;
 
-    TRACE("received control %d\n", dwControl);
-    
+    TRACE("%s control %u\n", debugstr_w(service->name), dwControl);
+
     if (service->handler)
         ret = service->handler(dwControl, 0, NULL, service->context);
-    return WriteFile(pipe, &ret, sizeof ret, &count, NULL);
+    return ret;
 }
 
 /******************************************************************************
@@ -478,61 +470,108 @@
  */
 static DWORD WINAPI service_control_dispatcher(LPVOID arg)
 {
-    service_data *service = arg;
+    SC_HANDLE manager;
     HANDLE pipe;
 
-    TRACE("%p %s\n", service, debugstr_w(service->name));
+    if (!(manager = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT )))
+    {
+        ERR("failed to open service manager error %u\n", GetLastError());
+        return 0;
+    }
 
     pipe = service_open_pipe();
 
     if (pipe==INVALID_HANDLE_VALUE)
     {
-        ERR("failed to create pipe for %s, error = %d\n",
-            debugstr_w(service->name), GetLastError());
+        ERR("failed to create control pipe error = %d\n", GetLastError());
         return 0;
     }
 
     /* dispatcher loop */
     while (1)
     {
+        service_data *service;
+        service_start_info info;
+        WCHAR *data = NULL;
         BOOL r;
-        DWORD count, req[2] = {0,0};
+        DWORD data_size = 0, count, result;
 
-        r = ReadFile( pipe, &req, sizeof req, &count, NULL );
+        r = ReadFile( pipe, &info, FIELD_OFFSET(service_start_info,data), &count, NULL );
         if (!r)
         {
             if (GetLastError() != ERROR_BROKEN_PIPE)
                 ERR( "pipe read failed error %u\n", GetLastError() );
             break;
         }
-        if (count != sizeof(req))
+        if (count != FIELD_OFFSET(service_start_info,data))
         {
             ERR( "partial pipe read %u\n", count );
             break;
         }
+        if (count < info.total_size)
+        {
+            data_size = info.total_size - FIELD_OFFSET(service_start_info,data);
+            data = HeapAlloc( GetProcessHeap(), 0, data_size );
+            r = ReadFile( pipe, data, data_size, &count, NULL );
+            if (!r)
+            {
+                if (GetLastError() != ERROR_BROKEN_PIPE)
+                    ERR( "pipe read failed error %u\n", GetLastError() );
+                break;
+            }
+            if (count != data_size)
+            {
+                ERR( "partial pipe read %u/%u\n", count, data_size );
+                break;
+            }
+        }
+
+        /* find the service */
+
+        if (!(service = find_service_by_name( data )))
+        {
+            FIXME( "got request %u for unknown service %s\n", info.cmd, debugstr_w(data));
+            result = ERROR_INVALID_PARAMETER;
+            goto done;
+        }
+
+        TRACE( "got request %u for service %s\n", info.cmd, debugstr_w(data) );
 
         /* handle the request */
-        switch (req[0])
+        switch (info.cmd)
         {
         case WINESERV_STARTINFO:
-            service_handle_start(pipe, service, req[1]);
+            if (!service->handle)
+            {
+                if (!(service->handle = OpenServiceW( manager, data, SERVICE_SET_STATUS )))
+                    FIXME( "failed to open service %s\n", debugstr_w(data) );
+            }
+            result = service_handle_start(service, data + info.name_size,
+                                          data_size / sizeof(WCHAR) - info.name_size );
             break;
         case WINESERV_SENDCONTROL:
-            service_handle_control(pipe, service, req[1]);
+            result = service_handle_control(service, info.control);
             break;
         default:
-            ERR("received invalid command %d length %d\n", req[0], req[1]);
+            ERR("received invalid command %u\n", info.cmd);
+            result = ERROR_INVALID_PARAMETER;
+            break;
         }
+
+    done:
+        WriteFile(pipe, &result, sizeof(result), &count, NULL);
+        HeapFree( GetProcessHeap(), 0, data );
     }
 
     CloseHandle(pipe);
+    CloseServiceHandle( manager );
     return 1;
 }
 
 /******************************************************************************
- * service_run_threads
+ * service_run_main_thread
  */
-static BOOL service_run_threads(void)
+static BOOL service_run_main_thread(void)
 {
     DWORD i, n, ret;
     HANDLE wait_handles[MAXIMUM_WAIT_OBJECTS];
@@ -540,22 +579,19 @@
 
     service_event = CreateEventW( NULL, FALSE, FALSE, NULL );
 
+    /* FIXME: service_control_dispatcher should be merged into the main thread */
     wait_handles[0] = __wine_make_process_system();
-    wait_handles[1] = service_event;
+    wait_handles[1] = CreateThread( NULL, 0, service_control_dispatcher, NULL, 0, NULL );
+    wait_handles[2] = service_event;
 
-    TRACE("Starting %d pipe listener threads. Services running as process %d\n",
+    TRACE("Starting %d services running as process %d\n",
           nb_services, GetCurrentProcessId());
 
-    EnterCriticalSection( &service_cs );
-    for (i = 0; i < nb_services; i++)
-        CloseHandle( CreateThread( NULL, 0, service_control_dispatcher, services[i], 0, NULL ));
-    LeaveCriticalSection( &service_cs );
-
     /* wait for all the threads to pack up and exit */
     for (;;)
     {
         EnterCriticalSection( &service_cs );
-        for (i = 0, n = 2; i < nb_services && n < MAXIMUM_WAIT_OBJECTS; i++)
+        for (i = 0, n = 3; i < nb_services && n < MAXIMUM_WAIT_OBJECTS; i++)
         {
             if (!services[i]->thread) continue;
             wait_services[n] = i;
@@ -572,13 +608,19 @@
         }
         else if (ret == 1)
         {
+            TRACE( "control dispatcher exited, shutting down\n" );
+            /* FIXME: we should maybe send a shutdown control to running services */
+            ExitProcess(0);
+        }
+        else if (ret == 2)
+        {
             continue;  /* rebuild the list */
         }
         else if (ret < n)
         {
             services[wait_services[ret]]->thread = 0;
             CloseHandle( wait_handles[ret] );
-            if (n == 3) return TRUE; /* it was the last running thread */
+            if (n == 4) return TRUE; /* it was the last running thread */
         }
         else return FALSE;
     }
@@ -616,7 +658,7 @@
         services[i] = info;
     }
 
-    service_run_threads();
+    service_run_main_thread();
 
     return ret;
 }
@@ -661,7 +703,7 @@
         services[i] = info;
     }
 
-    service_run_threads();
+    service_run_main_thread();
 
     return ret;
 }
@@ -2214,40 +2256,23 @@
 SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerExW( LPCWSTR lpServiceName,
         LPHANDLER_FUNCTION_EX lpHandlerProc, LPVOID lpContext )
 {
-    SC_HANDLE hService;
-    SC_HANDLE hSCM;
-    unsigned int i;
+    service_data *service;
+    SC_HANDLE hService = 0;
     BOOL found = FALSE;
 
     TRACE("%s %p %p\n", debugstr_w(lpServiceName), lpHandlerProc, lpContext);
 
-    hSCM = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
-    if (!hSCM)
-        return NULL;
-    hService = OpenServiceW( hSCM, lpServiceName, SERVICE_SET_STATUS );
-    CloseServiceHandle(hSCM);
-    if (!hService)
-        return NULL;
-
     EnterCriticalSection( &service_cs );
-    for (i = 0; i < nb_services; i++)
+    if ((service = find_service_by_name( lpServiceName )))
     {
-        if(!strcmpW(lpServiceName, services[i]->name))
-        {
-            services[i]->handler = lpHandlerProc;
-            services[i]->context = lpContext;
-            found = TRUE;
-            break;
-        }
+        service->handler = lpHandlerProc;
+        service->context = lpContext;
+        hService = service->handle;
+        found = TRUE;
     }
     LeaveCriticalSection( &service_cs );
 
-    if (!found)
-    {
-        CloseServiceHandle(hService);
-        SetLastError(ERROR_SERVICE_DOES_NOT_EXIST);
-        return NULL;
-    }
+    if (!found) SetLastError(ERROR_SERVICE_DOES_NOT_EXIST);
 
     return (SERVICE_STATUS_HANDLE)hService;
 }
diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl
index 443f6c9..65dfb62 100644
--- a/include/wine/svcctl.idl
+++ b/include/wine/svcctl.idl
@@ -32,15 +32,20 @@
 cpp_quote("#define SVCCTL_STARTED_EVENT {'_','_','w','i','n','e','_','S','v','c','c','t','l','S','t','a','r','t','e','d',0}")
 
 /* Service startup protocol over control pipe - not compatible with Windows */
-cpp_quote("typedef struct service_start_info_t")
-cpp_quote("{")
-cpp_quote("    DWORD cmd;")
-cpp_quote("    DWORD size;")
-cpp_quote("    WCHAR str[1];")
-cpp_quote("} service_start_info;")
+enum service_pipe_command
+{
+    WINESERV_STARTINFO = 1,
+    WINESERV_SENDCONTROL = 2
+};
 
-cpp_quote("#define WINESERV_STARTINFO   1")
-cpp_quote("#define WINESERV_SENDCONTROL 2")
+typedef struct service_start_info_t
+{
+    enum service_pipe_command cmd;        /* request code */
+    DWORD                     total_size; /* total request size */
+    DWORD                     name_size;  /* size of name in data buffer */
+    DWORD                     control;    /* control code */
+    WCHAR                     data[1];
+} service_start_info;
 
 [
     uuid(367abb81-9844-35f1-ad32-98f038001003),
diff --git a/programs/services/rpc.c b/programs/services/rpc.c
index 616daa9..3176a1e 100644
--- a/programs/services/rpc.c
+++ b/programs/services/rpc.c
@@ -708,15 +708,24 @@
 /******************************************************************************
  * service_send_control
  */
-static BOOL service_send_control(HANDLE pipe, DWORD dwControl, DWORD *result)
+static BOOL service_send_control(struct service_entry *service, HANDLE pipe, DWORD dwControl, DWORD *result)
 {
-    DWORD cmd[2], count = 0;
+    service_start_info *ssi;
+    DWORD len, count = 0;
     BOOL r;
 
-    cmd[0] = WINESERV_SENDCONTROL;
-    cmd[1] = dwControl;
-    r = WriteFile(pipe, cmd, sizeof cmd, &count, NULL);
-    if (!r || count != sizeof cmd)
+    /* calculate how much space we need to send the startup info */
+    len = strlenW(service->name) + 1;
+
+    ssi = HeapAlloc(GetProcessHeap(),0,FIELD_OFFSET(service_start_info, data[len]));
+    ssi->cmd = WINESERV_SENDCONTROL;
+    ssi->control = dwControl;
+    ssi->total_size = FIELD_OFFSET(service_start_info, data[len]);
+    ssi->name_size = strlenW(service->name) + 1;
+    strcpyW( ssi->data, service->name );
+
+    r = WriteFile(pipe, ssi, ssi->total_size, &count, NULL);
+    if (!r || count != ssi->total_size)
     {
         WINE_ERR("service protocol error - failed to write pipe!\n");
         return r;
@@ -837,7 +846,7 @@
     {
         DWORD result = ERROR_SUCCESS;
 
-        ret = service_send_control(control_pipe, dwControl, &result);
+        ret = service_send_control(service->service_entry, control_pipe, dwControl, &result);
 
         if (dwControl == SERVICE_CONTROL_STOP)
         {
diff --git a/programs/services/services.c b/programs/services/services.c
index 5b5fbc0..806fbb6 100644
--- a/programs/services/services.c
+++ b/programs/services/services.c
@@ -621,17 +621,17 @@
 /******************************************************************************
  * service_send_start_message
  */
-static BOOL service_send_start_message(HANDLE pipe, LPCWSTR *argv, DWORD argc)
+static BOOL service_send_start_message(struct service_entry *service, LPCWSTR *argv, DWORD argc)
 {
     DWORD i, len, count, result;
     service_start_info *ssi;
     LPWSTR p;
     BOOL r;
 
-    WINE_TRACE("%p %p %d\n", pipe, argv, argc);
+    WINE_TRACE("%s %p %d\n", wine_dbgstr_w(service->name), argv, argc);
 
     /* FIXME: this can block so should be done in another thread */
-    r = ConnectNamedPipe(pipe, NULL);
+    r = ConnectNamedPipe(service->control_pipe, NULL);
     if (!r && GetLastError() != ERROR_PIPE_CONNECTED)
     {
         WINE_ERR("pipe connect failed\n");
@@ -639,16 +639,20 @@
     }
 
     /* calculate how much space do we need to send the startup info */
-    len = 1;
+    len = strlenW(service->name) + 1;
     for (i=0; i<argc; i++)
         len += strlenW(argv[i])+1;
+    len++;
 
-    ssi = HeapAlloc(GetProcessHeap(),0,FIELD_OFFSET(service_start_info, str[len]));
+    ssi = HeapAlloc(GetProcessHeap(),0,FIELD_OFFSET(service_start_info, data[len]));
     ssi->cmd = WINESERV_STARTINFO;
-    ssi->size = len;
+    ssi->control = 0;
+    ssi->total_size = FIELD_OFFSET(service_start_info, data[len]);
+    ssi->name_size = strlenW(service->name) + 1;
+    strcpyW( ssi->data, service->name );
 
     /* copy service args into a single buffer*/
-    p = &ssi->str[0];
+    p = &ssi->data[ssi->name_size];
     for (i=0; i<argc; i++)
     {
         strcpyW(p, argv[i]);
@@ -656,10 +660,10 @@
     }
     *p=0;
 
-    r = WriteFile(pipe, ssi, FIELD_OFFSET(service_start_info, str[len]), &count, NULL);
+    r = WriteFile(service->control_pipe, ssi, ssi->total_size, &count, NULL);
     if (r)
     {
-        r = ReadFile(pipe, &result, sizeof result, &count, NULL);
+        r = ReadFile(service->control_pipe, &result, sizeof result, &count, NULL);
         if (r && result)
         {
             SetLastError(result);
@@ -709,8 +713,7 @@
 
     if (err == ERROR_SUCCESS)
     {
-        if (!service_send_start_message(service->control_pipe,
-                service_argv, service_argc))
+        if (!service_send_start_message(service, service_argv, service_argc))
             err = ERROR_SERVICE_REQUEST_TIMEOUT;
     }