ADVAPI32: convert services to use pipes for RPC instead of shared
memory
Mike McCormack
mike at codeweavers.com
Sun Jan 16 23:22:39 CST 2005
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 -r1.82 service.c
--- dlls/advapi32/service.c 14 Jan 2005 16:50:57 -0000 1.82
+++ dlls/advapi32/service.c 17 Jan 2005 05:14:56 -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,43 @@
'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
+
+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 +184,7 @@
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 +208,349 @@
HeapFree( GetProcessHeap(), 0, wstr );
}
+
/******************************************************************************
- * read_scm_lock_data
- *
- * helper function for service control dispatcher
- *
- * SCM database is locked by StartService;
- * open global SCM lock object and read service name
+ * Service IPC functions
*/
-static BOOL read_scm_lock_data( LPWSTR buffer )
+static LPWSTR service_get_pipe_name(LPWSTR service)
{
- HANDLE hLock;
- LPWSTR argptr;
+ static const WCHAR prefix[] = { '\\','\\','.','\\','p','i','p','e','\\',
+ '_','_','w','i','n','e','s','e','r','v','i','c','e','_',0};
+ LPWSTR name;
+ DWORD len;
- hLock = OpenFileMappingW( FILE_MAP_ALL_ACCESS, FALSE, szSCMLock );
- if( NULL == hLock )
- {
- SetLastError( ERROR_FAILED_SERVICE_CONTROLLER_CONNECT );
- return FALSE;
- }
- argptr = MapViewOfFile( hLock, FILE_MAP_ALL_ACCESS,
- 0, 0, MAX_SERVICE_NAME * sizeof(WCHAR) );
- if( NULL == argptr )
- {
- CloseHandle( hLock );
- return FALSE;
- }
- strcpyW( buffer, argptr );
- UnmapViewOfFile( argptr );
- CloseHandle( hLock );
- return TRUE;
+ len = sizeof prefix + strlenW(service)*sizeof(WCHAR);
+ name = HeapAlloc(GetProcessHeap(), 0, len);
+ strcpyW(name, prefix);
+ strcatW(name, service);
+ return name;
+}
+
+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;
}
/******************************************************************************
- * open_seb_shmem
+ * service_thread
*
- * helper function for service control dispatcher
+ * Call into the main service routine provided by StartServiceCtrlDispatcher.
*/
-static struct SEB* open_seb_shmem( LPWSTR service_name, HANDLE* hServiceShmem )
+static DWORD WINAPI service_thread(LPVOID arg)
{
- WCHAR object_name[ MAX_PATH ];
- HANDLE hmem;
- struct SEB *ret;
+ service_data *info = arg;
+ LPWSTR str = info->args;
+ DWORD argc = 0, len = 0;
- snprintfW( object_name, MAX_PATH, szServiceShmemNameFmtW, service_name );
- hmem = OpenFileMappingW( FILE_MAP_ALL_ACCESS, FALSE, object_name );
- if( NULL == hmem )
- return NULL;
+ TRACE("%p\n", arg);
- ret = MapViewOfFile( hmem, FILE_MAP_ALL_ACCESS, 0, 0, 0 );
- if( NULL == ret )
- CloseHandle( hmem );
+ while (str[len])
+ {
+ len += strlenW(&str[len]) + 1;
+ argc++;
+ }
+
+ if (info->unicode)
+ {
+ 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);
+ }
else
- *hServiceShmem = hmem;
- return ret;
+ {
+ 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;
}
/******************************************************************************
- * 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_handle_start
*/
-static LPWSTR* build_arg_vectors( struct SEB* seb )
+static BOOL service_handle_start(HANDLE pipe, service_data *service, DWORD count)
{
- LPWSTR *ret;
- LPWSTR argptr;
- DWORD i;
+ DWORD read = 0, result = 0;
+ LPWSTR args;
+ BOOL r;
- ret = HeapAlloc( GetProcessHeap(), 0, (1 + seb->argc) * sizeof(LPWSTR) );
- if( NULL == ret )
- return NULL;
+ TRACE("%p %p %ld\n", pipe, service, count);
- argptr = (LPWSTR) &seb[1];
- for( i = 0; i < seb->argc; i++ )
+ 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])
{
- ret[ 1 + i ] = argptr;
- argptr += 1 + strlenW( argptr );
+ 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;
}
- return ret;
-}
-/******************************************************************************
- * service thread
- */
-struct service_thread_data
-{
- 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;
-};
+ if (service->thread)
+ {
+ ERR("service is not stopped\n");
+ goto end;
+ }
-static DWORD WINAPI service_thread( LPVOID arg )
-{
- struct service_thread_data *data = arg;
+ if (service->args)
+ SERV_free(service->args);
+ service->args = args;
+ args = NULL;
+ service->thread = CreateThread( NULL, 0, service_thread,
+ service, 0, NULL );
- data->service_main( data->argc, data->argv );
- SetEvent( dispatcher_event );
- return 0;
+end:
+ HeapFree(GetProcessHeap(), 0, args);
+ WriteFile( pipe, &result, sizeof result, &read, NULL );
+
+ return TRUE;
}
/******************************************************************************
- * dispose_service_thread_data
- *
- * helper function for service control dispatcher
+ * service_handle_get_status
*/
-static void dispose_service_thread_data( struct service_thread_data* thread_data )
+static BOOL service_handle_get_status(HANDLE pipe, service_data *service)
{
- 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 count = 0;
+ TRACE("\n");
+ return WriteFile(pipe, &service->status,
+ sizeof service->status, &count, NULL);
}
/******************************************************************************
- * start_new_service
- *
- * helper function for service control dispatcher
+ * service_reap_thread
*/
-static struct service_thread_data*
-start_new_service( LPSERVICE_MAIN_FUNCTIONW service_main, BOOL ascii )
+static DWORD service_reap_thread(service_data *service)
{
- struct service_thread_data *thread_data;
- unsigned int i;
- WCHAR object_name[ MAX_PATH ];
+ DWORD exitcode = 0;
- thread_data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct service_thread_data) );
- if( NULL == thread_data )
- return NULL;
-
- if( ! read_scm_lock_data( thread_data->service_name ) )
+ if (!service->thread)
+ return 0;
+ GetExitCodeThread(service->thread, &exitcode);
+ if (exitcode!=STILL_ACTIVE)
{
- /* 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;
+ CloseHandle(service->thread);
+ service->thread = 0;
}
+ return exitcode;
+}
- thread_data->seb = open_seb_shmem( thread_data->service_name, &thread_data->hServiceShmem );
- if( NULL == thread_data->seb )
- goto error;
+/******************************************************************************
+ * service_control_dispatcher
+ */
+static DWORD WINAPI service_control_dispatcher(LPVOID arg)
+{
+ service_data *service = arg;
+ LPWSTR name;
+ HANDLE pipe, event;
- thread_data->argv = build_arg_vectors( thread_data->seb );
- if( NULL == thread_data->argv )
- goto error;
+ TRACE("%p %s\n", service, debugstr_w(service->name));
+
+ /* 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);
+
+ /* 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);
- thread_data->argv[0] = thread_data->service_name;
- thread_data->argc = thread_data->seb->argc + 1;
+ if (pipe==INVALID_HANDLE_VALUE)
+ {
+ ERR("failed to create pipe, error = %ld\n", GetLastError());
+ return 0;
+ }
- if( ascii )
+ /* dispatcher loop */
+ while (1)
{
- /* 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;
+ BOOL r;
+ DWORD count, req[2] = {0,0};
- for(i=1; i<thread_data->argc; i++)
+ r = ConnectNamedPipe(pipe, NULL);
+ if (!r && GetLastError() != ERROR_PIPE_CONNECTED)
{
- 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 );
+ ERR("pipe connect failed\n");
+ break;
}
- }
- /* 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;
+ r = ReadFile( pipe, &req, sizeof req, &count, NULL );
+ if (!r || count!=sizeof req)
+ {
+ ERR("pipe read failed\n");
+ break;
+ }
- if( ERROR_ALREADY_EXISTS == GetLastError() )
- {
- SetLastError( ERROR_SERVICE_ALREADY_RUNNING );
- goto error;
- }
+ service_reap_thread(service);
- /* 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;
+ /* handle the request */
+ switch (req[0])
+ {
+ case WINESERV_STARTINFO:
+ service_handle_start(pipe, service, req[1]);
+ break;
+ case WINESERV_GETSTATUS:
+ service_handle_get_status(pipe, service);
+ break;
+ default:
+ ERR("received invalid command %ld length %ld\n", req[0], req[1]);
+ }
- if( ERROR_ALREADY_EXISTS == GetLastError() )
- {
- SetLastError( ERROR_SERVICE_ALREADY_RUNNING );
- goto error;
+ FlushFileBuffers(pipe);
+ DisconnectNamedPipe(pipe);
}
- /* 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;
+ CloseHandle(pipe);
+ return 1;
}
/******************************************************************************
- * service_ctrl_dispatcher
+ * service_run_threads
*/
-static BOOL service_ctrl_dispatcher( LPSERVICE_TABLE_ENTRYW servent, BOOL ascii )
+static BOOL service_run_threads(void)
{
- WCHAR object_name[ MAX_PATH ];
- HANDLE wait;
+ service_data *service;
+ DWORD count = 0, n = 0;
+ HANDLE *handles;
- /* FIXME: if shared service, find entry by service name */
-
- /* FIXME: move this into dispatcher loop */
- service = start_new_service( servent->lpServiceProc, ascii );
- if( NULL == service )
- return FALSE;
+ EnterCriticalSection( &service_cs );
- ResumeThread( service->thread_handle );
-
- /* 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;
- }
+ /* count how many services there are */
+ for (service = service_list; service; service = service->next)
+ count++;
- if( ERROR_ALREADY_EXISTS == GetLastError() )
- {
- SetLastError( ERROR_SERVICE_ALREADY_RUNNING );
- CloseHandle( dispatcher_event );
- return FALSE;
- }
+ TRACE("starting %ld pipe listener threads\n", count);
- /* ready to accept control requests */
- ReleaseMutex( service->mutex );
+ handles = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE)*count);
- /* signal for StartService */
- wait = OpenSemaphoreW( SEMAPHORE_MODIFY_STATE, FALSE, szWaitServiceStartW );
- if( wait )
- {
- ReleaseSemaphore( wait, 1, NULL );
- CloseHandle( wait );
- }
+ 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);
- /* dispatcher loop */
- for(;;)
- {
- DWORD ret;
+ LeaveCriticalSection( &service_cs );
- WaitForSingleObject( dispatcher_event, INFINITE );
+ /* wait for all the threads to pack up and exit */
+ WaitForMultipleObjectsEx(count, handles, TRUE, INFINITE, FALSE);
- /* 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 )
- {
- CloseHandle( service->thread_handle );
- dispose_service_thread_data( service );
- break;
- }
-
- /* look for control requests */
- if( service->seb->control_code )
- {
- 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 );
- }
+ HeapFree(GetProcessHeap(), 0, handles);
- /* FIXME: if shared service, check SCM lock object;
- * if exists, a new service should be started */
- }
-
- CloseHandle( dispatcher_event );
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 )
+BOOL WINAPI StartServiceCtrlDispatcherA( LPSERVICE_TABLE_ENTRYA servent )
{
- int count, i;
- LPSERVICE_TABLE_ENTRYW ServiceTableW;
- BOOL ret;
-
- TRACE("(%p)\n", servent);
+ service_data *info;
+ DWORD sz, len;
+ BOOL ret = TRUE;
- /* convert service table to unicode */
- for( count = 0; servent[ count ].lpServiceName; )
- count++;
- ServiceTableW = HeapAlloc( GetProcessHeap(), 0, (count + 1) * sizeof(SERVICE_TABLE_ENTRYW) );
- if( NULL == ServiceTableW )
- return FALSE;
+ TRACE("%p\n", servent);
- for( i = 0; i < count; i++ )
+ 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 +562,7 @@
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 +582,14 @@
{
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 +607,20 @@
* 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 +633,32 @@
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 +766,7 @@
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;
hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE);
if (!hsvc)
@@ -782,79 +775,9 @@
return FALSE;
}
- TRACE("%p(%s) %ld %p\n", hService, debugstr_w(hsvc->name),
+ FIXME("%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 )
- {
- SetLastError( ERROR_SERVICE_NOT_ACTIVE );
- return FALSE;
- }
-
- r = WaitForSingleObject( mutex, 30000 );
- if( WAIT_FAILED == r )
- goto done;
-
- if( WAIT_TIMEOUT == r )
- {
- 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;
- }
-
- if( seb->dispatcher_error )
- {
- SetLastError( seb->dispatcher_error );
- goto done;
- }
-
- /* 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 +1132,213 @@
* 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.
+ * SERV_StartServiceProcess [INTERNAL]
*/
-BOOL WINAPI
-StartServiceW( SC_HANDLE hService, DWORD dwNumServiceArgs,
- LPCWSTR *lpServiceArgVectors )
+static DWORD SERV_StartServiceProcess(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;
+ 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);
+ }
+ HeapFree(GetProcessHeap(),0,str);
+ if (!path)
+ return FALSE;
- TRACE("%p %ld %p\n",hService,dwNumServiceArgs,
- lpServiceArgVectors);
+ /* 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;
+ }
- hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE);
- if (!hsvc)
- {
- SetLastError( ERROR_INVALID_HANDLE );
- return FALSE;
+ CloseHandle( pi.hThread );
+ CloseHandle( pi.hProcess );
}
+ CloseHandle( handles[0] );
+ HeapFree(GetProcessHeap(),0,path);
+ return r;
+}
- size = sizeof(str);
- r = RegQueryValueExW(hsvc->hkey, _ImagePathW, NULL, &type, (LPVOID)str, &size);
- if (r!=ERROR_SUCCESS)
- return FALSE;
- ExpandEnvironmentStringsW(str,path,sizeof(path));
+static HANDLE service_open_pipe(LPWSTR service)
+{
+ LPWSTR szPipe = service_get_pipe_name( service );
+ HANDLE handle = INVALID_HANDLE_VALUE;
- TRACE("Starting service %s\n", debugstr_w(path) );
+ 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);
- hLock = LockServiceDatabase( (SC_HANDLE) &hsvc->scm->hdr );
- if( NULL == hLock )
- return FALSE;
+ return handle;
+}
- /*
- * FIXME: start dependent services
- */
+/******************************************************************************
+ * service_get_status
+ */
+static BOOL service_get_status(HANDLE pipe, LPSERVICE_STATUS status)
+{
+ 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;
+}
- /* 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;
- }
- strcpyW( shmem_lock, hsvc->name );
+/******************************************************************************
+ * service_send_start_message
+ */
+static BOOL service_send_start_message(HANDLE pipe, LPCWSTR *argv, DWORD argc)
+{
+ DWORD i, len, count, result;
+ service_start_info *ssi;
+ LPWSTR p;
+ BOOL r;
- /* create service environment block */
- size = sizeof(struct SEB);
- for( i = 0; i < dwNumServiceArgs; i++ )
- size += sizeof(WCHAR) * (1 + strlenW( lpServiceArgVectors[ i ] ));
+ TRACE("%p %p %ld\n", pipe, argv, argc);
- 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;
- }
+ /* 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;
- /* 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 );
- }
+ ssi = HeapAlloc(GetProcessHeap(),0,sizeof *ssi + (len-1)*sizeof(WCHAR));
+ ssi->cmd = WINESERV_STARTINFO;
+ ssi->size = len;
- wait = CreateSemaphoreW(NULL,0,1,szWaitServiceStartW);
- if (!wait)
+ /* copy service args into a single buffer*/
+ p = &ssi->str[0];
+ for (i=0; i<argc; i++)
{
- ERR("Couldn't create wait semaphore\n");
- goto done;
+ strcpyW(p, argv[i]);
+ p += strlenW(p) + 1;
}
+ *p=0;
- ZeroMemory(&startupinfo,sizeof(STARTUPINFOW));
- startupinfo.cb = sizeof(STARTUPINFOW);
+ r = WriteFile(pipe, ssi, sizeof *ssi + len*sizeof(WCHAR), &count, NULL);
+ if (r)
+ r = ReadFile(pipe, &result, sizeof result, &count, NULL);
- 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 */
+ HeapFree(GetProcessHeap(),0,ssi);
- if(r == FALSE)
- {
- ERR("Couldn't start process\n");
- goto done;
- }
- CloseHandle( procinfo.hThread );
+ return r;
+}
- /* docs for StartServiceCtrlDispatcher say this should be 30 sec */
- r = WaitForSingleObject(wait,30000);
- if( WAIT_FAILED == r )
+/******************************************************************************
+ * 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;
+
+ TRACE("%p %ld %p\n", hService, dwNumServiceArgs, lpServiceArgVectors);
+
+ hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE);
+ if (!hsvc)
{
- CloseHandle( procinfo.hProcess );
- goto done;
+ SetLastError(ERROR_INVALID_HANDLE);
+ return r;
}
- if( WAIT_TIMEOUT == r )
+
+ hLock = LockServiceDatabase(hsvc->scm);
+ if (!hLock)
+ return r;
+
+ handle = service_open_pipe(hsvc->name);
+ if (handle==INVALID_HANDLE_VALUE)
{
- TerminateProcess( procinfo.hProcess, 1 );
- CloseHandle( procinfo.hProcess );
- SetLastError( ERROR_SERVICE_REQUEST_TIMEOUT );
- goto done;
+ /* start the service process */
+ r = SERV_StartServiceProcess(hsvc);
+ if (!r)
+ goto done;
+
+ handle = service_open_pipe(hsvc->name);
+ if (handle==INVALID_HANDLE_VALUE)
+ goto done;
}
- /* allright */
- CloseHandle( procinfo.hProcess );
- ret = TRUE;
+ service_send_start_message(handle, lpServiceArgVectors, dwNumServiceArgs);
done:
- if( wait ) CloseHandle( wait );
- if( seb != NULL ) UnmapViewOfFile( seb );
- if( hServiceShmem != NULL ) CloseHandle( hServiceShmem );
- if( shmem_lock != NULL ) UnmapViewOfFile( shmem_lock );
+ if (handle != INVALID_HANDLE_VALUE)
+ CloseHandle(handle);
UnlockServiceDatabase( hLock );
- return ret;
+ return r;
}
/******************************************************************************
@@ -1398,16 +1349,15 @@
* 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,42 +1366,14 @@
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;
+ pipe = service_open_pipe(hsvc->name);
+ r = service_get_status(pipe, lpservicestatus);
+ CloseHandle(pipe);
+ if (r)
+ return TRUE;
+
+ TRACE("Failed to read service status\n");
-done:
- if( seb ) UnmapViewOfFile( seb );
- if( shmem ) CloseHandle( shmem );
- if( mutex_owned ) ReleaseMutex( mutex );
- CloseHandle( mutex );
- return ret;
-
-stopped:
- /* service stopped */
/* read the service type from the registry */
size = sizeof(val);
r = RegQueryValueExA(hsvc->hkey, "Type", NULL, &type, (LPBYTE)&val, &size);
More information about the wine-patches
mailing list