|  | /* | 
|  | * Win32 advapi functions | 
|  | * | 
|  | * Copyright 1995 Sven Verdoolaege | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | */ | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include <string.h> | 
|  | #include <time.h> | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "winsvc.h" | 
|  | #include "winerror.h" | 
|  | #include "winreg.h" | 
|  | #include "wine/unicode.h" | 
|  | #include "wine/debug.h" | 
|  | #include "winternl.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(advapi); | 
|  |  | 
|  | static const WCHAR szServiceManagerKey[] = { 'S','y','s','t','e','m','\\', | 
|  | 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', | 
|  | '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 */ | 
|  | SERVICE_STATUS status; | 
|  | DWORD argc; | 
|  | /* variable part of SEB contains service arguments */ | 
|  | }; | 
|  |  | 
|  | 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 */ | 
|  |  | 
|  | /****************************************************************************** | 
|  | * SC_HANDLEs | 
|  | */ | 
|  |  | 
|  | #define MAX_SERVICE_NAME 256 | 
|  |  | 
|  | typedef enum { SC_HTYPE_MANAGER, SC_HTYPE_SERVICE } SC_HANDLE_TYPE; | 
|  |  | 
|  | struct sc_handle; | 
|  |  | 
|  | struct sc_manager       /* SCM handle */ | 
|  | { | 
|  | HKEY hkey_scm_db;   /* handle to services database in the registry */ | 
|  | LONG ref_count;     /* handle must remain alive until any related service */ | 
|  | /* handle exists because DeleteService requires it */ | 
|  | }; | 
|  |  | 
|  | struct sc_service       /* service handle */ | 
|  | { | 
|  | HKEY hkey;          /* handle to service entry in the registry (under hkey_scm_db) */ | 
|  | struct sc_handle *sc_manager;  /* pointer to SCM handle */ | 
|  | WCHAR name[ MAX_SERVICE_NAME ]; | 
|  | }; | 
|  |  | 
|  | struct sc_handle | 
|  | { | 
|  | SC_HANDLE_TYPE htype; | 
|  | union | 
|  | { | 
|  | struct sc_manager manager; | 
|  | struct sc_service service; | 
|  | } u; | 
|  | }; | 
|  |  | 
|  | static struct sc_handle* alloc_sc_handle( SC_HANDLE_TYPE htype ) | 
|  | { | 
|  | struct sc_handle *retval; | 
|  |  | 
|  | retval = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct sc_handle) ); | 
|  | if( retval != NULL ) | 
|  | { | 
|  | retval->htype = htype; | 
|  | } | 
|  | TRACE("SC_HANDLE type=%d -> %p\n",htype,retval); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | static void free_sc_handle( struct sc_handle* handle ) | 
|  | { | 
|  | if( NULL == handle ) | 
|  | return; | 
|  |  | 
|  | switch( handle->htype ) | 
|  | { | 
|  | case SC_HTYPE_MANAGER: | 
|  | { | 
|  | if( InterlockedDecrement( &handle->u.manager.ref_count ) ) | 
|  | /* there are references to this handle */ | 
|  | return; | 
|  |  | 
|  | if( handle->u.manager.hkey_scm_db ) | 
|  | RegCloseKey( handle->u.manager.hkey_scm_db ); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case SC_HTYPE_SERVICE: | 
|  | { | 
|  | struct sc_handle *h = handle->u.service.sc_manager; | 
|  |  | 
|  | if( h ) | 
|  | { | 
|  | /* release SCM handle */ | 
|  | if( 0 == InterlockedDecrement( &h->u.manager.ref_count ) ) | 
|  | { | 
|  | /* it's time to destroy SCM handle */ | 
|  | if( h->u.manager.hkey_scm_db ) | 
|  | RegCloseKey( h->u.manager.hkey_scm_db ); | 
|  |  | 
|  | TRACE("SC_HANDLE (SCM) %p type=%d\n",h,h->htype); | 
|  |  | 
|  | HeapFree( GetProcessHeap(), 0, h ); | 
|  | } | 
|  | } | 
|  | if( handle->u.service.hkey ) | 
|  | RegCloseKey( handle->u.service.hkey ); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | TRACE("SC_HANDLE %p type=%d\n",handle,handle->htype); | 
|  |  | 
|  | HeapFree( GetProcessHeap(), 0, handle ); | 
|  | } | 
|  |  | 
|  | static void init_service_handle( struct sc_handle* handle, | 
|  | struct sc_handle* sc_manager, | 
|  | HKEY hKey, LPCWSTR lpServiceName ) | 
|  | { | 
|  | /* init sc_service structure */ | 
|  | handle->u.service.hkey = hKey; | 
|  | lstrcpynW( handle->u.service.name, lpServiceName, MAX_SERVICE_NAME ); | 
|  |  | 
|  | /* add reference to SCM handle */ | 
|  | InterlockedIncrement( &sc_manager->u.manager.ref_count ); | 
|  | handle->u.service.sc_manager = sc_manager; | 
|  | } | 
|  |  | 
|  | static inline LPWSTR SERV_dup( LPCSTR str ) | 
|  | { | 
|  | UINT len; | 
|  | LPWSTR wstr; | 
|  |  | 
|  | if( !str ) | 
|  | return NULL; | 
|  | len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); | 
|  | wstr = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) ); | 
|  | MultiByteToWideChar( CP_ACP, 0, str, -1, wstr, len ); | 
|  | return wstr; | 
|  | } | 
|  |  | 
|  | static inline LPWSTR SERV_dupmulti( LPCSTR str ) | 
|  | { | 
|  | UINT len = 0, n = 0; | 
|  | LPWSTR wstr; | 
|  |  | 
|  | if( !str ) | 
|  | return NULL; | 
|  | do { | 
|  | len += MultiByteToWideChar( CP_ACP, 0, &str[n], -1, NULL, 0 ); | 
|  | n += (strlen( &str[n] ) + 1); | 
|  | } while (str[n]); | 
|  | len++; | 
|  | n++; | 
|  |  | 
|  | wstr = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) ); | 
|  | MultiByteToWideChar( CP_ACP, 0, str, n, wstr, len ); | 
|  | return wstr; | 
|  | } | 
|  |  | 
|  | static inline VOID SERV_free( LPWSTR wstr ) | 
|  | { | 
|  | HeapFree( GetProcessHeap(), 0, wstr ); | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * EnumServicesStatusA [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI | 
|  | EnumServicesStatusA( SC_HANDLE hSCManager, DWORD dwServiceType, | 
|  | DWORD dwServiceState, LPENUM_SERVICE_STATUSA lpServices, | 
|  | DWORD cbBufSize, LPDWORD pcbBytesNeeded, | 
|  | LPDWORD lpServicesReturned, LPDWORD lpResumeHandle ) | 
|  | {	FIXME("%p type=%lx state=%lx %p %lx %p %p %p\n", hSCManager, | 
|  | dwServiceType, dwServiceState, lpServices, cbBufSize, | 
|  | pcbBytesNeeded, lpServicesReturned,  lpResumeHandle); | 
|  | SetLastError (ERROR_ACCESS_DENIED); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * EnumServicesStatusW [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI | 
|  | EnumServicesStatusW( SC_HANDLE hSCManager, DWORD dwServiceType, | 
|  | DWORD dwServiceState, LPENUM_SERVICE_STATUSW lpServices, | 
|  | DWORD cbBufSize, LPDWORD pcbBytesNeeded, | 
|  | LPDWORD lpServicesReturned, LPDWORD lpResumeHandle ) | 
|  | {	FIXME("%p type=%lx state=%lx %p %lx %p %p %p\n", hSCManager, | 
|  | dwServiceType, dwServiceState, lpServices, cbBufSize, | 
|  | pcbBytesNeeded, lpServicesReturned,  lpResumeHandle); | 
|  | SetLastError (ERROR_ACCESS_DENIED); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * 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 | 
|  | */ | 
|  | static BOOL read_scm_lock_data( LPWSTR buffer ) | 
|  | { | 
|  | HANDLE hLock; | 
|  | LPWSTR argptr; | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * open_seb_shmem | 
|  | * | 
|  | * helper function for service control dispatcher | 
|  | */ | 
|  | static struct SEB* open_seb_shmem( LPWSTR service_name, HANDLE* hServiceShmem ) | 
|  | { | 
|  | WCHAR object_name[ MAX_PATH ]; | 
|  | HANDLE hmem; | 
|  | struct SEB *ret; | 
|  |  | 
|  | snprintfW( object_name, MAX_PATH, szServiceShmemNameFmtW, service_name ); | 
|  | hmem = OpenFileMappingW( FILE_MAP_ALL_ACCESS, FALSE, object_name ); | 
|  | if( NULL == hmem ) | 
|  | return NULL; | 
|  |  | 
|  | ret = MapViewOfFile( hmem, FILE_MAP_ALL_ACCESS, 0, 0, 0 ); | 
|  | if( NULL == ret ) | 
|  | CloseHandle( hmem ); | 
|  | else | 
|  | *hServiceShmem = hmem; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * 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. | 
|  | */ | 
|  | static LPWSTR* build_arg_vectors( struct SEB* seb ) | 
|  | { | 
|  | LPWSTR *ret; | 
|  | LPWSTR argptr; | 
|  | DWORD i; | 
|  |  | 
|  | ret = HeapAlloc( GetProcessHeap(), 0, (1 + seb->argc) * sizeof(LPWSTR) ); | 
|  | if( NULL == ret ) | 
|  | return NULL; | 
|  |  | 
|  | argptr = (LPWSTR) &seb[1]; | 
|  | for( i = 0; i < seb->argc; i++ ) | 
|  | { | 
|  | ret[ 1 + i ] = argptr; | 
|  | argptr += 1 + strlenW( argptr ); | 
|  | } | 
|  | 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; | 
|  | }; | 
|  |  | 
|  | static DWORD WINAPI service_thread( LPVOID arg ) | 
|  | { | 
|  | struct service_thread_data *data = arg; | 
|  |  | 
|  | data->service_main( data->argc, data->argv ); | 
|  | SetEvent( dispatcher_event ); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * dispose_service_thread_data | 
|  | * | 
|  | * helper function for service control dispatcher | 
|  | */ | 
|  | static void dispose_service_thread_data( struct service_thread_data* thread_data ) | 
|  | { | 
|  | 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 ); | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * start_new_service | 
|  | * | 
|  | * helper function for service control dispatcher | 
|  | */ | 
|  | static struct service_thread_data* | 
|  | start_new_service( LPSERVICE_MAIN_FUNCTIONW service_main, BOOL ascii ) | 
|  | { | 
|  | 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; | 
|  |  | 
|  | if( ! read_scm_lock_data( thread_data->service_name ) ) | 
|  | { | 
|  | /* 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; | 
|  | } | 
|  |  | 
|  | 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 ) | 
|  | { | 
|  | /* 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 ); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* 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; | 
|  |  | 
|  | if( ERROR_ALREADY_EXISTS == GetLastError() ) | 
|  | { | 
|  | SetLastError( ERROR_SERVICE_ALREADY_RUNNING ); | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | /* 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; | 
|  |  | 
|  | if( ERROR_ALREADY_EXISTS == GetLastError() ) | 
|  | { | 
|  | SetLastError( ERROR_SERVICE_ALREADY_RUNNING ); | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | /* 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; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * service_ctrl_dispatcher | 
|  | */ | 
|  | static BOOL service_ctrl_dispatcher( LPSERVICE_TABLE_ENTRYW servent, BOOL ascii ) | 
|  | { | 
|  | WCHAR object_name[ MAX_PATH ]; | 
|  | HANDLE wait; | 
|  |  | 
|  | /* 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; | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | if( ERROR_ALREADY_EXISTS == GetLastError() ) | 
|  | { | 
|  | 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 ); | 
|  | } | 
|  |  | 
|  | /* dispatcher loop */ | 
|  | for(;;) | 
|  | { | 
|  | DWORD ret; | 
|  |  | 
|  | WaitForSingleObject( dispatcher_event, INFINITE ); | 
|  |  | 
|  | /* 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 ); | 
|  | } | 
|  |  | 
|  | /* FIXME: if shared service, check SCM lock object; | 
|  | * if exists, a new service should be started */ | 
|  | } | 
|  |  | 
|  | CloseHandle( dispatcher_event ); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * StartServiceCtrlDispatcherA [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI | 
|  | StartServiceCtrlDispatcherA( LPSERVICE_TABLE_ENTRYA servent ) | 
|  | { | 
|  | int count, i; | 
|  | LPSERVICE_TABLE_ENTRYW ServiceTableW; | 
|  | BOOL ret; | 
|  |  | 
|  | TRACE("(%p)\n", servent); | 
|  |  | 
|  | /* 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; | 
|  |  | 
|  | for( i = 0; i < count; i++ ) | 
|  | { | 
|  | ServiceTableW[ i ].lpServiceName = SERV_dup( servent[ i ].lpServiceName ); | 
|  | ServiceTableW[ i ].lpServiceProc = (LPSERVICE_MAIN_FUNCTIONW) servent[ i ].lpServiceProc; | 
|  | } | 
|  | ServiceTableW[ count ].lpServiceName = NULL; | 
|  | ServiceTableW[ count ].lpServiceProc = NULL; | 
|  |  | 
|  | /* start dispatcher */ | 
|  | ret = service_ctrl_dispatcher( ServiceTableW, TRUE ); | 
|  |  | 
|  | /* free service table */ | 
|  | for( i = 0; i < count; i++ ) | 
|  | SERV_free( ServiceTableW[ i ].lpServiceName ); | 
|  | HeapFree( GetProcessHeap(), 0, ServiceTableW ); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * StartServiceCtrlDispatcherW [ADVAPI32.@] | 
|  | * | 
|  | * PARAMS | 
|  | *   servent [] | 
|  | */ | 
|  | BOOL WINAPI | 
|  | StartServiceCtrlDispatcherW( LPSERVICE_TABLE_ENTRYW servent ) | 
|  | { | 
|  | TRACE("(%p)\n", servent); | 
|  | return service_ctrl_dispatcher( servent, FALSE ); | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * LockServiceDatabase  [ADVAPI32.@] | 
|  | */ | 
|  | LPVOID WINAPI LockServiceDatabase (SC_HANDLE hSCManager) | 
|  | { | 
|  | HANDLE ret; | 
|  |  | 
|  | TRACE("%p\n",hSCManager); | 
|  |  | 
|  | ret = CreateFileMappingW( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, | 
|  | 0, MAX_SERVICE_NAME * sizeof(WCHAR), szSCMLock ); | 
|  | if( ret && GetLastError() == ERROR_ALREADY_EXISTS ) | 
|  | { | 
|  | CloseHandle( ret ); | 
|  | ret = NULL; | 
|  | SetLastError( ERROR_SERVICE_DATABASE_LOCKED ); | 
|  | } | 
|  |  | 
|  | TRACE("returning %p\n", ret); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * UnlockServiceDatabase  [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI UnlockServiceDatabase (LPVOID ScLock) | 
|  | { | 
|  | TRACE("%p\n",ScLock); | 
|  |  | 
|  | return CloseHandle( (HANDLE) ScLock ); | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * RegisterServiceCtrlHandlerA [ADVAPI32.@] | 
|  | */ | 
|  | SERVICE_STATUS_HANDLE WINAPI | 
|  | RegisterServiceCtrlHandlerA( LPCSTR lpServiceName, | 
|  | LPHANDLER_FUNCTION lpfHandler ) | 
|  | { | 
|  | LPWSTR lpServiceNameW; | 
|  | SERVICE_STATUS_HANDLE ret; | 
|  |  | 
|  | lpServiceNameW = SERV_dup(lpServiceName); | 
|  | ret = RegisterServiceCtrlHandlerW( lpServiceNameW, lpfHandler ); | 
|  | SERV_free(lpServiceNameW); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * RegisterServiceCtrlHandlerW [ADVAPI32.@] | 
|  | * | 
|  | * PARAMS | 
|  | *   lpServiceName [] | 
|  | *   lpfHandler    [] | 
|  | */ | 
|  | SERVICE_STATUS_HANDLE WINAPI | 
|  | RegisterServiceCtrlHandlerW( LPCWSTR lpServiceName, | 
|  | LPHANDLER_FUNCTION lpfHandler ) | 
|  | { | 
|  | /* FIXME: find service thread data by service name */ | 
|  |  | 
|  | service->ctrl_handler = lpfHandler; | 
|  | return 0xcacacafe; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * SetServiceStatus [ADVAPI32.@] | 
|  | * | 
|  | * PARAMS | 
|  | *   hService [] | 
|  | *   lpStatus [] | 
|  | */ | 
|  | BOOL WINAPI | 
|  | SetServiceStatus( SERVICE_STATUS_HANDLE hService, LPSERVICE_STATUS lpStatus ) | 
|  | { | 
|  | DWORD r; | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * OpenSCManagerA [ADVAPI32.@] | 
|  | * | 
|  | * Establish a connection to the service control manager and open its database. | 
|  | * | 
|  | * PARAMS | 
|  | *   lpMachineName   [I] Pointer to machine name string | 
|  | *   lpDatabaseName  [I] Pointer to database name string | 
|  | *   dwDesiredAccess [I] Type of access | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: A Handle to the service control manager database | 
|  | *   Failure: NULL | 
|  | */ | 
|  | SC_HANDLE WINAPI OpenSCManagerA( LPCSTR lpMachineName, LPCSTR lpDatabaseName, | 
|  | DWORD dwDesiredAccess ) | 
|  | { | 
|  | LPWSTR lpMachineNameW, lpDatabaseNameW; | 
|  | SC_HANDLE ret; | 
|  |  | 
|  | lpMachineNameW = SERV_dup(lpMachineName); | 
|  | lpDatabaseNameW = SERV_dup(lpDatabaseName); | 
|  | ret = OpenSCManagerW(lpMachineNameW, lpDatabaseNameW, dwDesiredAccess); | 
|  | SERV_free(lpDatabaseNameW); | 
|  | SERV_free(lpMachineNameW); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * OpenSCManagerW [ADVAPI32.@] | 
|  | * | 
|  | * See OpenSCManagerA. | 
|  | */ | 
|  | SC_HANDLE WINAPI OpenSCManagerW( LPCWSTR lpMachineName, LPCWSTR lpDatabaseName, | 
|  | DWORD dwDesiredAccess ) | 
|  | { | 
|  | struct sc_handle *retval; | 
|  | HKEY hReg; | 
|  | LONG r; | 
|  |  | 
|  | TRACE("(%s,%s,0x%08lx)\n", debugstr_w(lpMachineName), | 
|  | debugstr_w(lpDatabaseName), dwDesiredAccess); | 
|  |  | 
|  | if( lpDatabaseName && lpDatabaseName[0] ) | 
|  | { | 
|  | if( strcmpiW( lpDatabaseName, SERVICES_ACTIVE_DATABASEW ) == 0 ) | 
|  | { | 
|  | /* noop, all right */ | 
|  | } | 
|  | else if( strcmpiW( lpDatabaseName, SERVICES_FAILED_DATABASEW ) == 0 ) | 
|  | { | 
|  | SetLastError( ERROR_DATABASE_DOES_NOT_EXIST ); | 
|  | return NULL; | 
|  | } | 
|  | else | 
|  | { | 
|  | SetLastError( ERROR_INVALID_NAME ); | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | retval = alloc_sc_handle( SC_HTYPE_MANAGER ); | 
|  | if( NULL == retval ) return NULL; | 
|  |  | 
|  | retval->u.manager.ref_count = 1; | 
|  |  | 
|  | r = RegConnectRegistryW(lpMachineName,HKEY_LOCAL_MACHINE,&hReg); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | goto error; | 
|  |  | 
|  | r = RegOpenKeyExW(hReg, szServiceManagerKey, | 
|  | 0, KEY_ALL_ACCESS, &retval->u.manager.hkey_scm_db); | 
|  | RegCloseKey( hReg ); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | goto error; | 
|  |  | 
|  | TRACE("returning %p\n", retval); | 
|  |  | 
|  | return (SC_HANDLE) retval; | 
|  |  | 
|  | error: | 
|  | free_sc_handle( retval ); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * ControlService [ADVAPI32.@] | 
|  | * | 
|  | * Send a control code to a service. | 
|  | * | 
|  | * PARAMS | 
|  | *   hService        [I] Handle of the service control manager database | 
|  | *   dwControl       [I] Control code to send (SERVICE_CONTROL_* flags from "winsvc.h") | 
|  | *   lpServiceStatus [O] Destination for the status of the service, if available | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: TRUE. | 
|  | *   Failure: FALSE. | 
|  | * | 
|  | * BUGS | 
|  | *   Unlike M$' implementation, control requests are not serialized and may be | 
|  | *   processed asynchronously. | 
|  | */ | 
|  | BOOL WINAPI ControlService( SC_HANDLE hService, DWORD dwControl, | 
|  | LPSERVICE_STATUS lpServiceStatus ) | 
|  | { | 
|  | struct sc_handle *hsvc = hService; | 
|  | 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; | 
|  |  | 
|  | /* open and hold mutex */ | 
|  | snprintfW( object_name, MAX_PATH, szServiceMutexNameFmtW, hsvc->u.service.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->u.service.name ); | 
|  | disp_event = OpenEventW( EVENT_ALL_ACCESS, FALSE, object_name ); | 
|  | if( NULL == disp_event ) | 
|  | goto done; | 
|  |  | 
|  | snprintfW( object_name, MAX_PATH, szServiceAckEventNameFmtW, hsvc->u.service.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->u.service.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; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * CloseServiceHandle [ADVAPI32.@] | 
|  | * | 
|  | * Close a handle to a service or the service control manager database. | 
|  | * | 
|  | * PARAMS | 
|  | *   hSCObject [I] Handle to service or service control manager database | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: TRUE | 
|  | *  Failure: FALSE | 
|  | */ | 
|  | BOOL WINAPI | 
|  | CloseServiceHandle( SC_HANDLE hSCObject ) | 
|  | { | 
|  | TRACE("(%p)\n", hSCObject); | 
|  |  | 
|  | free_sc_handle( (struct sc_handle*) hSCObject ); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  |  | 
|  | /****************************************************************************** | 
|  | * OpenServiceA [ADVAPI32.@] | 
|  | * | 
|  | * Open a handle to a service. | 
|  | * | 
|  | * PARAMS | 
|  | *   hSCManager      [I] Handle of the service control manager database | 
|  | *   lpServiceName   [I] Name of the service to open | 
|  | *   dwDesiredAccess [I] Access required to the service | 
|  | * | 
|  | * RETURNS | 
|  | *    Success: Handle to the service | 
|  | *    Failure: NULL | 
|  | */ | 
|  | SC_HANDLE WINAPI OpenServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName, | 
|  | DWORD dwDesiredAccess ) | 
|  | { | 
|  | LPWSTR lpServiceNameW; | 
|  | SC_HANDLE ret; | 
|  |  | 
|  | TRACE("%p %s %ld\n",hSCManager, debugstr_a(lpServiceName), dwDesiredAccess); | 
|  |  | 
|  | lpServiceNameW = SERV_dup(lpServiceName); | 
|  | ret = OpenServiceW( hSCManager, lpServiceNameW, dwDesiredAccess); | 
|  | SERV_free(lpServiceNameW); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  |  | 
|  | /****************************************************************************** | 
|  | * OpenServiceW [ADVAPI32.@] | 
|  | * | 
|  | * See OpenServiceA. | 
|  | */ | 
|  | SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName, | 
|  | DWORD dwDesiredAccess) | 
|  | { | 
|  | struct sc_handle *hscm = hSCManager; | 
|  | struct sc_handle *retval; | 
|  | HKEY hKey; | 
|  | long r; | 
|  |  | 
|  | TRACE("%p %s %ld\n",hSCManager, debugstr_w(lpServiceName), dwDesiredAccess); | 
|  |  | 
|  | if (!lpServiceName) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_ADDRESS); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | retval = alloc_sc_handle( SC_HTYPE_SERVICE ); | 
|  | if( NULL == retval ) | 
|  | return NULL; | 
|  |  | 
|  | r = RegOpenKeyExW( hscm->u.manager.hkey_scm_db, | 
|  | lpServiceName, 0, KEY_ALL_ACCESS, &hKey ); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | { | 
|  | free_sc_handle( retval ); | 
|  | SetLastError( ERROR_SERVICE_DOES_NOT_EXIST ); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | init_service_handle( retval, hscm, hKey, lpServiceName ); | 
|  |  | 
|  | TRACE("returning %p\n",retval); | 
|  |  | 
|  | return (SC_HANDLE) retval; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * CreateServiceW [ADVAPI32.@] | 
|  | */ | 
|  | SC_HANDLE WINAPI | 
|  | CreateServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName, | 
|  | LPCWSTR lpDisplayName, DWORD dwDesiredAccess, | 
|  | DWORD dwServiceType, DWORD dwStartType, | 
|  | DWORD dwErrorControl, LPCWSTR lpBinaryPathName, | 
|  | LPCWSTR lpLoadOrderGroup, LPDWORD lpdwTagId, | 
|  | LPCWSTR lpDependencies, LPCWSTR lpServiceStartName, | 
|  | LPCWSTR lpPassword ) | 
|  | { | 
|  | struct sc_handle *hscm = hSCManager; | 
|  | struct sc_handle *retval; | 
|  | HKEY hKey; | 
|  | LONG r; | 
|  | DWORD dp; | 
|  | static const WCHAR szDisplayName[] = { 'D','i','s','p','l','a','y','N','a','m','e', 0 }; | 
|  | static const WCHAR szType[] = {'T','y','p','e',0}; | 
|  | static const WCHAR szStart[] = {'S','t','a','r','t',0}; | 
|  | static const WCHAR szError[] = {'E','r','r','o','r','C','o','n','t','r','o','l', 0}; | 
|  | static const WCHAR szImagePath[] = {'I','m','a','g','e','P','a','t','h',0}; | 
|  | static const WCHAR szGroup[] = {'G','r','o','u','p',0}; | 
|  | static const WCHAR szDependencies[] = { 'D','e','p','e','n','d','e','n','c','i','e','s',0}; | 
|  |  | 
|  | FIXME("%p %s %s\n", hSCManager, | 
|  | debugstr_w(lpServiceName), debugstr_w(lpDisplayName)); | 
|  |  | 
|  | retval = alloc_sc_handle( SC_HTYPE_SERVICE ); | 
|  | if( NULL == retval ) | 
|  | return NULL; | 
|  |  | 
|  | r = RegCreateKeyExW(hscm->u.manager.hkey_scm_db, lpServiceName, 0, NULL, | 
|  | REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dp); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | goto error; | 
|  |  | 
|  | init_service_handle( retval, hscm, hKey, lpServiceName ); | 
|  |  | 
|  | if (dp != REG_CREATED_NEW_KEY) | 
|  | { | 
|  | SetLastError(ERROR_SERVICE_EXISTS); | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | if(lpDisplayName) | 
|  | { | 
|  | r = RegSetValueExW(hKey, szDisplayName, 0, REG_SZ, (const BYTE*)lpDisplayName, | 
|  | (strlenW(lpDisplayName)+1)*sizeof(WCHAR) ); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | r = RegSetValueExW(hKey, szType, 0, REG_DWORD, (LPVOID)&dwServiceType, sizeof (DWORD) ); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | goto error; | 
|  |  | 
|  | r = RegSetValueExW(hKey, szStart, 0, REG_DWORD, (LPVOID)&dwStartType, sizeof (DWORD) ); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | goto error; | 
|  |  | 
|  | r = RegSetValueExW(hKey, szError, 0, REG_DWORD, | 
|  | (LPVOID)&dwErrorControl, sizeof (DWORD) ); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | goto error; | 
|  |  | 
|  | if(lpBinaryPathName) | 
|  | { | 
|  | r = RegSetValueExW(hKey, szImagePath, 0, REG_SZ, (const BYTE*)lpBinaryPathName, | 
|  | (strlenW(lpBinaryPathName)+1)*sizeof(WCHAR) ); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | if(lpLoadOrderGroup) | 
|  | { | 
|  | r = RegSetValueExW(hKey, szGroup, 0, REG_SZ, (const BYTE*)lpLoadOrderGroup, | 
|  | (strlenW(lpLoadOrderGroup)+1)*sizeof(WCHAR) ); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | if(lpDependencies) | 
|  | { | 
|  | DWORD len = 0; | 
|  |  | 
|  | /* determine the length of a double null terminated multi string */ | 
|  | do { | 
|  | len += (strlenW(&lpDependencies[len])+1); | 
|  | } while (lpDependencies[len++]); | 
|  |  | 
|  | r = RegSetValueExW(hKey, szDependencies, 0, REG_MULTI_SZ, | 
|  | (const BYTE*)lpDependencies, len ); | 
|  | if (r!=ERROR_SUCCESS) | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | if(lpPassword) | 
|  | { | 
|  | FIXME("Don't know how to add a Password for a service.\n"); | 
|  | } | 
|  |  | 
|  | if(lpServiceStartName) | 
|  | { | 
|  | FIXME("Don't know how to add a ServiceStartName for a service.\n"); | 
|  | } | 
|  |  | 
|  | return (SC_HANDLE) retval; | 
|  |  | 
|  | error: | 
|  | free_sc_handle( retval ); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | /****************************************************************************** | 
|  | * CreateServiceA [ADVAPI32.@] | 
|  | */ | 
|  | SC_HANDLE WINAPI | 
|  | CreateServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName, | 
|  | LPCSTR lpDisplayName, DWORD dwDesiredAccess, | 
|  | DWORD dwServiceType, DWORD dwStartType, | 
|  | DWORD dwErrorControl, LPCSTR lpBinaryPathName, | 
|  | LPCSTR lpLoadOrderGroup, LPDWORD lpdwTagId, | 
|  | LPCSTR lpDependencies, LPCSTR lpServiceStartName, | 
|  | LPCSTR lpPassword ) | 
|  | { | 
|  | LPWSTR lpServiceNameW, lpDisplayNameW, lpBinaryPathNameW, | 
|  | lpLoadOrderGroupW, lpDependenciesW, lpServiceStartNameW, lpPasswordW; | 
|  | SC_HANDLE r; | 
|  |  | 
|  | TRACE("%p %s %s\n", hSCManager, | 
|  | debugstr_a(lpServiceName), debugstr_a(lpDisplayName)); | 
|  |  | 
|  | lpServiceNameW = SERV_dup( lpServiceName ); | 
|  | lpDisplayNameW = SERV_dup( lpDisplayName ); | 
|  | lpBinaryPathNameW = SERV_dup( lpBinaryPathName ); | 
|  | lpLoadOrderGroupW = SERV_dup( lpLoadOrderGroup ); | 
|  | lpDependenciesW = SERV_dupmulti( lpDependencies ); | 
|  | lpServiceStartNameW = SERV_dup( lpServiceStartName ); | 
|  | lpPasswordW = SERV_dup( lpPassword ); | 
|  |  | 
|  | r = CreateServiceW( hSCManager, lpServiceNameW, lpDisplayNameW, | 
|  | dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl, | 
|  | lpBinaryPathNameW, lpLoadOrderGroupW, lpdwTagId, | 
|  | lpDependenciesW, lpServiceStartNameW, lpPasswordW ); | 
|  |  | 
|  | SERV_free( lpServiceNameW ); | 
|  | SERV_free( lpDisplayNameW ); | 
|  | SERV_free( lpBinaryPathNameW ); | 
|  | SERV_free( lpLoadOrderGroupW ); | 
|  | SERV_free( lpDependenciesW ); | 
|  | SERV_free( lpServiceStartNameW ); | 
|  | SERV_free( lpPasswordW ); | 
|  |  | 
|  | return r; | 
|  | } | 
|  |  | 
|  |  | 
|  | /****************************************************************************** | 
|  | * DeleteService [ADVAPI32.@] | 
|  | * | 
|  | * Delete a service from the service control manager database. | 
|  | * | 
|  | * PARAMS | 
|  | *    hService [I] Handle of the service to delete | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: TRUE | 
|  | *  Failure: FALSE | 
|  | */ | 
|  | BOOL WINAPI DeleteService( SC_HANDLE hService ) | 
|  | { | 
|  | struct sc_handle *hsvc = hService; | 
|  | HKEY hKey = hsvc->u.service.hkey; | 
|  | WCHAR valname[MAX_PATH+1]; | 
|  | INT index = 0; | 
|  | LONG rc; | 
|  | DWORD size; | 
|  |  | 
|  | size = MAX_PATH+1; | 
|  | /* Clean out the values */ | 
|  | rc = RegEnumValueW(hKey, index, valname,&size,0,0,0,0); | 
|  | while (rc == ERROR_SUCCESS) | 
|  | { | 
|  | RegDeleteValueW(hKey,valname); | 
|  | index++; | 
|  | size = MAX_PATH+1; | 
|  | rc = RegEnumValueW(hKey, index, valname, &size,0,0,0,0); | 
|  | } | 
|  |  | 
|  | RegCloseKey(hKey); | 
|  | hsvc->u.service.hkey = NULL; | 
|  |  | 
|  | /* delete the key */ | 
|  | RegDeleteKeyW(hsvc->u.service.sc_manager->u.manager.hkey_scm_db, | 
|  | hsvc->u.service.name); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  |  | 
|  | /****************************************************************************** | 
|  | * StartServiceA [ADVAPI32.@] | 
|  | * | 
|  | * Start a service | 
|  | * | 
|  | * PARAMS | 
|  | *   hService            [I] Handle of service | 
|  | *   dwNumServiceArgs    [I] Number of arguments | 
|  | *   lpServiceArgVectors [I] Address of array of argument strings | 
|  | * | 
|  | * NOTES | 
|  | *  - NT implements this function using an obscure RPC call. | 
|  | *  - You might need to do a "setenv SystemRoot \\WINNT" in your .cshrc | 
|  | *    to get things like "%SystemRoot%\\System32\\service.exe" to load. | 
|  | *  - This will only work for shared address space. How should the service | 
|  | *    args be transferred when address spaces are separated? | 
|  | *  - Can only start one service at a time. | 
|  | *  - Has no concept of privilege. | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: TRUE. | 
|  | *   Failure: FALSE | 
|  | */ | 
|  | BOOL WINAPI | 
|  | StartServiceA( SC_HANDLE hService, DWORD dwNumServiceArgs, | 
|  | LPCSTR *lpServiceArgVectors ) | 
|  | { | 
|  | LPWSTR *lpwstr=NULL; | 
|  | unsigned int i; | 
|  |  | 
|  | TRACE("(%p,%ld,%p)\n",hService,dwNumServiceArgs,lpServiceArgVectors); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | if(dwNumServiceArgs) | 
|  | { | 
|  | for(i=0; i<dwNumServiceArgs; i++) | 
|  | SERV_free(lpwstr[i]); | 
|  | HeapFree(GetProcessHeap(), 0, lpwstr); | 
|  | } | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  |  | 
|  | /****************************************************************************** | 
|  | * StartServiceW [ADVAPI32.@] | 
|  | * | 
|  | * See StartServiceA. | 
|  | */ | 
|  | BOOL WINAPI | 
|  | StartServiceW( SC_HANDLE hService, DWORD dwNumServiceArgs, | 
|  | LPCWSTR *lpServiceArgVectors ) | 
|  | { | 
|  | static const WCHAR  _ImagePathW[]  = {'I','m','a','g','e','P','a','t','h',0}; | 
|  |  | 
|  | struct sc_handle *hsvc = hService; | 
|  | 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); | 
|  |  | 
|  | size = sizeof(str); | 
|  | r = RegQueryValueExW(hsvc->u.service.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( hsvc->u.service.sc_manager ); | 
|  | if( NULL == hLock ) | 
|  | return FALSE; | 
|  |  | 
|  | /* | 
|  | * FIXME: start dependent services | 
|  | */ | 
|  |  | 
|  | /* 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->u.service.name ); | 
|  |  | 
|  | /* create service environment block */ | 
|  | size = sizeof(struct SEB); | 
|  | for( i = 0; i < dwNumServiceArgs; i++ ) | 
|  | size += sizeof(WCHAR) * (1 + strlenW( lpServiceArgVectors[ i ] )); | 
|  |  | 
|  | snprintfW( str, MAX_PATH, szServiceShmemNameFmtW, hsvc->u.service.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; | 
|  | } | 
|  |  | 
|  | /* 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 ); | 
|  | } | 
|  |  | 
|  | wait = CreateSemaphoreW(NULL,0,1,szWaitServiceStartW); | 
|  | if (!wait) | 
|  | { | 
|  | ERR("Couldn't create wait semaphore\n"); | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | ZeroMemory(&startupinfo,sizeof(STARTUPINFOW)); | 
|  | startupinfo.cb = sizeof(STARTUPINFOW); | 
|  |  | 
|  | 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 */ | 
|  |  | 
|  | if(r == FALSE) | 
|  | { | 
|  | ERR("Couldn't start process\n"); | 
|  | goto done; | 
|  | } | 
|  | CloseHandle( procinfo.hThread ); | 
|  |  | 
|  | /* docs for StartServiceCtrlDispatcher say this should be 30 sec */ | 
|  | r = WaitForSingleObject(wait,30000); | 
|  | if( WAIT_FAILED == r ) | 
|  | { | 
|  | CloseHandle( procinfo.hProcess ); | 
|  | goto done; | 
|  | } | 
|  | if( WAIT_TIMEOUT == r ) | 
|  | { | 
|  | TerminateProcess( procinfo.hProcess, 1 ); | 
|  | CloseHandle( procinfo.hProcess ); | 
|  | SetLastError( ERROR_SERVICE_REQUEST_TIMEOUT ); | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | /* 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; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * QueryServiceStatus [ADVAPI32.@] | 
|  | * | 
|  | * PARAMS | 
|  | *   hService        [] | 
|  | *   lpservicestatus [] | 
|  | * | 
|  | */ | 
|  | BOOL WINAPI | 
|  | QueryServiceStatus( SC_HANDLE hService, LPSERVICE_STATUS lpservicestatus ) | 
|  | { | 
|  | struct sc_handle *hsvc = hService; | 
|  | 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; | 
|  |  | 
|  | /* try to open service mutex */ | 
|  | snprintfW( object_name, MAX_PATH, szServiceMutexNameFmtW, hsvc->u.service.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->u.service.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 */ | 
|  | /* read the service type from the registry */ | 
|  | size = sizeof(val); | 
|  | r = RegQueryValueExA(hsvc->u.service.hkey, "Type", NULL, &type, (LPBYTE)&val, &size); | 
|  | if(type!=REG_DWORD) | 
|  | { | 
|  | ERR("invalid Type\n"); | 
|  | return FALSE; | 
|  | } | 
|  | lpservicestatus->dwServiceType = val; | 
|  | lpservicestatus->dwCurrentState            = 1;  /* stopped */ | 
|  | lpservicestatus->dwControlsAccepted        = 0; | 
|  | lpservicestatus->dwWin32ExitCode           = NO_ERROR; | 
|  | lpservicestatus->dwServiceSpecificExitCode = 0; | 
|  | lpservicestatus->dwCheckPoint              = 0; | 
|  | lpservicestatus->dwWaitHint                = 0; | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * QueryServiceStatusEx [ADVAPI32.@] | 
|  | * | 
|  | * Get information about a service. | 
|  | * | 
|  | * PARAMS | 
|  | *   hService       [I] Handle to service to get information about | 
|  | *   InfoLevel      [I] Level of information to get | 
|  | *   lpBuffer       [O] Destination for requested information | 
|  | *   cbBufSize      [I] Size of lpBuffer in bytes | 
|  | *   pcbBytesNeeded [O] Destination for number of bytes needed, if cbBufSize is too small | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: TRUE | 
|  | *  FAILURE: FALSE | 
|  | */ | 
|  | BOOL WINAPI QueryServiceStatusEx(SC_HANDLE hService, SC_STATUS_TYPE InfoLevel, | 
|  | LPBYTE lpBuffer, DWORD cbBufSize, | 
|  | LPDWORD pcbBytesNeeded) | 
|  | { | 
|  | FIXME("stub\n"); | 
|  | SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * QueryServiceConfigA [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI | 
|  | QueryServiceConfigA( SC_HANDLE hService, | 
|  | LPQUERY_SERVICE_CONFIGA lpServiceConfig, | 
|  | DWORD cbBufSize, LPDWORD pcbBytesNeeded) | 
|  | { | 
|  | static const CHAR szDisplayName[] = "DisplayName"; | 
|  | static const CHAR szType[] = "Type"; | 
|  | static const CHAR szStart[] = "Start"; | 
|  | static const CHAR szError[] = "ErrorControl"; | 
|  | static const CHAR szImagePath[] = "ImagePath"; | 
|  | static const CHAR szGroup[] = "Group"; | 
|  | static const CHAR szDependencies[] = "Dependencies"; | 
|  | HKEY hKey = ((struct sc_handle*) hService)->u.service.hkey; | 
|  | CHAR str_buffer[ MAX_PATH ]; | 
|  | LONG r; | 
|  | DWORD type, val, sz, total, n; | 
|  | LPBYTE p; | 
|  |  | 
|  | TRACE("%p %p %ld %p\n", hService, lpServiceConfig, | 
|  | cbBufSize, pcbBytesNeeded); | 
|  |  | 
|  | /* calculate the size required first */ | 
|  | total = sizeof (QUERY_SERVICE_CONFIGA); | 
|  |  | 
|  | sz = sizeof(str_buffer); | 
|  | r = RegQueryValueExA( hKey, szImagePath, 0, &type, str_buffer, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) ) | 
|  | { | 
|  | sz = ExpandEnvironmentStringsA(str_buffer,NULL,0); | 
|  | if( 0 == sz ) return FALSE; | 
|  |  | 
|  | total += sz; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* FIXME: set last error */ | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExA( hKey, szGroup, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExA( hKey, szDependencies, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_MULTI_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExA( hKey, szStart, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExA( hKey, szDisplayName, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | /* if there's not enough memory, return an error */ | 
|  | if( total > *pcbBytesNeeded ) | 
|  | { | 
|  | *pcbBytesNeeded = total; | 
|  | SetLastError( ERROR_INSUFFICIENT_BUFFER ); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | *pcbBytesNeeded = total; | 
|  | ZeroMemory( lpServiceConfig, total ); | 
|  |  | 
|  | sz = sizeof val; | 
|  | r = RegQueryValueExA( hKey, szType, 0, &type, (LPBYTE)&val, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) ) | 
|  | lpServiceConfig->dwServiceType = val; | 
|  |  | 
|  | sz = sizeof val; | 
|  | r = RegQueryValueExA( hKey, szStart, 0, &type, (LPBYTE)&val, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) ) | 
|  | lpServiceConfig->dwStartType = val; | 
|  |  | 
|  | sz = sizeof val; | 
|  | r = RegQueryValueExA( hKey, szError, 0, &type, (LPBYTE)&val, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) ) | 
|  | lpServiceConfig->dwErrorControl = val; | 
|  |  | 
|  | /* now do the strings */ | 
|  | p = (LPBYTE) &lpServiceConfig[1]; | 
|  | n = total - sizeof (QUERY_SERVICE_CONFIGA); | 
|  |  | 
|  | sz = sizeof(str_buffer); | 
|  | r = RegQueryValueExA( hKey, szImagePath, 0, &type, str_buffer, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) ) | 
|  | { | 
|  | sz = ExpandEnvironmentStringsA(str_buffer, p, n); | 
|  | if( 0 == sz || sz > n ) return FALSE; | 
|  |  | 
|  | lpServiceConfig->lpBinaryPathName = (LPSTR) p; | 
|  | p += sz; | 
|  | n -= sz; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* FIXME: set last error */ | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | sz = n; | 
|  | r = RegQueryValueExA( hKey, szGroup, 0, &type, p, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) ) | 
|  | { | 
|  | lpServiceConfig->lpLoadOrderGroup = (LPSTR) p; | 
|  | p += sz; | 
|  | n -= sz; | 
|  | } | 
|  |  | 
|  | sz = n; | 
|  | r = RegQueryValueExA( hKey, szDependencies, 0, &type, p, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) ) | 
|  | { | 
|  | lpServiceConfig->lpDependencies = (LPSTR) p; | 
|  | p += sz; | 
|  | n -= sz; | 
|  | } | 
|  |  | 
|  | if( n < 0 ) | 
|  | ERR("Buffer overflow!\n"); | 
|  |  | 
|  | TRACE("Image path = %s\n", lpServiceConfig->lpBinaryPathName ); | 
|  | TRACE("Group      = %s\n", lpServiceConfig->lpLoadOrderGroup ); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * QueryServiceConfigW [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI | 
|  | QueryServiceConfigW( SC_HANDLE hService, | 
|  | LPQUERY_SERVICE_CONFIGW lpServiceConfig, | 
|  | DWORD cbBufSize, LPDWORD pcbBytesNeeded) | 
|  | { | 
|  | static const WCHAR szDisplayName[] = { | 
|  | 'D','i','s','p','l','a','y','N','a','m','e', 0 }; | 
|  | static const WCHAR szType[] = {'T','y','p','e',0}; | 
|  | static const WCHAR szStart[] = {'S','t','a','r','t',0}; | 
|  | static const WCHAR szError[] = { | 
|  | 'E','r','r','o','r','C','o','n','t','r','o','l', 0}; | 
|  | static const WCHAR szImagePath[] = {'I','m','a','g','e','P','a','t','h',0}; | 
|  | static const WCHAR szGroup[] = {'G','r','o','u','p',0}; | 
|  | static const WCHAR szDependencies[] = { | 
|  | 'D','e','p','e','n','d','e','n','c','i','e','s',0}; | 
|  | HKEY hKey = ((struct sc_handle*) hService)->u.service.hkey; | 
|  | WCHAR str_buffer[ MAX_PATH ]; | 
|  | LONG r; | 
|  | DWORD type, val, sz, total, n; | 
|  | LPBYTE p; | 
|  |  | 
|  | TRACE("%p %p %ld %p\n", hService, lpServiceConfig, | 
|  | cbBufSize, pcbBytesNeeded); | 
|  |  | 
|  | /* calculate the size required first */ | 
|  | total = sizeof (QUERY_SERVICE_CONFIGW); | 
|  |  | 
|  | sz = sizeof(str_buffer); | 
|  | r = RegQueryValueExW( hKey, szImagePath, 0, &type, (LPBYTE) str_buffer, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) ) | 
|  | { | 
|  | sz = ExpandEnvironmentStringsW(str_buffer,NULL,0); | 
|  | if( 0 == sz ) return FALSE; | 
|  |  | 
|  | total += sizeof(WCHAR) * sz; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* FIXME: set last error */ | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExW( hKey, szGroup, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExW( hKey, szDependencies, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_MULTI_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExW( hKey, szStart, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | sz = 0; | 
|  | r = RegQueryValueExW( hKey, szDisplayName, 0, &type, NULL, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) ) | 
|  | total += sz; | 
|  |  | 
|  | /* if there's not enough memory, return an error */ | 
|  | if( total > *pcbBytesNeeded ) | 
|  | { | 
|  | *pcbBytesNeeded = total; | 
|  | SetLastError( ERROR_INSUFFICIENT_BUFFER ); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | *pcbBytesNeeded = total; | 
|  | ZeroMemory( lpServiceConfig, total ); | 
|  |  | 
|  | sz = sizeof val; | 
|  | r = RegQueryValueExW( hKey, szType, 0, &type, (LPBYTE)&val, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) ) | 
|  | lpServiceConfig->dwServiceType = val; | 
|  |  | 
|  | sz = sizeof val; | 
|  | r = RegQueryValueExW( hKey, szStart, 0, &type, (LPBYTE)&val, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) ) | 
|  | lpServiceConfig->dwStartType = val; | 
|  |  | 
|  | sz = sizeof val; | 
|  | r = RegQueryValueExW( hKey, szError, 0, &type, (LPBYTE)&val, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) ) | 
|  | lpServiceConfig->dwErrorControl = val; | 
|  |  | 
|  | /* now do the strings */ | 
|  | p = (LPBYTE) &lpServiceConfig[1]; | 
|  | n = total - sizeof (QUERY_SERVICE_CONFIGW); | 
|  |  | 
|  | sz = sizeof(str_buffer); | 
|  | r = RegQueryValueExW( hKey, szImagePath, 0, &type, (LPBYTE) str_buffer, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) ) | 
|  | { | 
|  | sz = ExpandEnvironmentStringsW(str_buffer, (LPWSTR) p, n); | 
|  | sz *= sizeof(WCHAR); | 
|  | if( 0 == sz || sz > n ) return FALSE; | 
|  |  | 
|  | lpServiceConfig->lpBinaryPathName = (LPWSTR) p; | 
|  | p += sz; | 
|  | n -= sz; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* FIXME: set last error */ | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | sz = n; | 
|  | r = RegQueryValueExW( hKey, szGroup, 0, &type, p, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) ) | 
|  | { | 
|  | lpServiceConfig->lpLoadOrderGroup = (LPWSTR) p; | 
|  | p += sz; | 
|  | n -= sz; | 
|  | } | 
|  |  | 
|  | sz = n; | 
|  | r = RegQueryValueExW( hKey, szDependencies, 0, &type, p, &sz ); | 
|  | if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) ) | 
|  | { | 
|  | lpServiceConfig->lpDependencies = (LPWSTR) p; | 
|  | p += sz; | 
|  | n -= sz; | 
|  | } | 
|  |  | 
|  | if( n < 0 ) | 
|  | ERR("Buffer overflow!\n"); | 
|  |  | 
|  | TRACE("Image path = %s\n", debugstr_w(lpServiceConfig->lpBinaryPathName) ); | 
|  | TRACE("Group      = %s\n", debugstr_w(lpServiceConfig->lpLoadOrderGroup) ); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  |  | 
|  | /****************************************************************************** | 
|  | * GetServiceDisplayNameA  [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI GetServiceDisplayNameA( SC_HANDLE hSCManager, LPCSTR lpServiceName, | 
|  | LPSTR lpDisplayName, LPDWORD lpcchBuffer) | 
|  | { | 
|  | FIXME("%p %s %p %p\n", hSCManager, | 
|  | debugstr_a(lpServiceName), lpDisplayName, lpcchBuffer); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  |  | 
|  | /****************************************************************************** | 
|  | * GetServiceDisplayNameW  [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI GetServiceDisplayNameW( SC_HANDLE hSCManager, LPCWSTR lpServiceName, | 
|  | LPWSTR lpDisplayName, LPDWORD lpcchBuffer) | 
|  | { | 
|  | FIXME("%p %s %p %p\n", hSCManager, | 
|  | debugstr_w(lpServiceName), lpDisplayName, lpcchBuffer); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  |  | 
|  | /****************************************************************************** | 
|  | * ChangeServiceConfigW  [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI ChangeServiceConfigW( SC_HANDLE hService, DWORD dwServiceType, | 
|  | DWORD dwStartType, DWORD dwErrorControl, LPCWSTR lpBinaryPathName, | 
|  | LPCWSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCWSTR lpDependencies, | 
|  | LPCWSTR lpServiceStartName, LPCWSTR lpPassword, LPCWSTR lpDisplayName) | 
|  | { | 
|  | FIXME("%p %ld %ld %ld %s %s %p %p %s %s %s\n", | 
|  | hService, dwServiceType, dwStartType, dwErrorControl, | 
|  | debugstr_w(lpBinaryPathName), debugstr_w(lpLoadOrderGroup), | 
|  | lpdwTagId, lpDependencies, debugstr_w(lpServiceStartName), | 
|  | debugstr_w(lpPassword), debugstr_w(lpDisplayName) ); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * ChangeServiceConfigA  [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI ChangeServiceConfigA( SC_HANDLE hService, DWORD dwServiceType, | 
|  | DWORD dwStartType, DWORD dwErrorControl, LPCSTR lpBinaryPathName, | 
|  | LPCSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCSTR lpDependencies, | 
|  | LPCSTR lpServiceStartName, LPCSTR lpPassword, LPCSTR lpDisplayName) | 
|  | { | 
|  | LPWSTR wBinaryPathName, wLoadOrderGroup, wDependencies; | 
|  | LPWSTR wServiceStartName, wPassword, wDisplayName; | 
|  | BOOL r; | 
|  |  | 
|  | TRACE("%p %ld %ld %ld %s %s %p %p %s %s %s\n", | 
|  | hService, dwServiceType, dwStartType, dwErrorControl, | 
|  | debugstr_a(lpBinaryPathName), debugstr_a(lpLoadOrderGroup), | 
|  | lpdwTagId, lpDependencies, debugstr_a(lpServiceStartName), | 
|  | debugstr_a(lpPassword), debugstr_a(lpDisplayName) ); | 
|  |  | 
|  | wBinaryPathName = SERV_dup( lpBinaryPathName ); | 
|  | wLoadOrderGroup = SERV_dup( lpLoadOrderGroup ); | 
|  | wDependencies = SERV_dupmulti( lpDependencies ); | 
|  | wServiceStartName = SERV_dup( lpServiceStartName ); | 
|  | wPassword = SERV_dup( lpPassword ); | 
|  | wDisplayName = SERV_dup( lpDisplayName ); | 
|  |  | 
|  | r = ChangeServiceConfigW( hService, dwServiceType, | 
|  | dwStartType, dwErrorControl, wBinaryPathName, | 
|  | wLoadOrderGroup, lpdwTagId, wDependencies, | 
|  | wServiceStartName, wPassword, wDisplayName); | 
|  |  | 
|  | SERV_free( wBinaryPathName ); | 
|  | SERV_free( wLoadOrderGroup ); | 
|  | SERV_free( wDependencies ); | 
|  | SERV_free( wServiceStartName ); | 
|  | SERV_free( wPassword ); | 
|  | SERV_free( wDisplayName ); | 
|  |  | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * ChangeServiceConfig2A  [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI ChangeServiceConfig2A( SC_HANDLE hService, DWORD dwInfoLevel, | 
|  | LPVOID lpInfo) | 
|  | { | 
|  | BOOL r = FALSE; | 
|  |  | 
|  | TRACE("%p %ld %p\n",hService, dwInfoLevel, lpInfo); | 
|  |  | 
|  | if (dwInfoLevel == SERVICE_CONFIG_DESCRIPTION) | 
|  | { | 
|  | LPSERVICE_DESCRIPTIONA sd = (LPSERVICE_DESCRIPTIONA) lpInfo; | 
|  | SERVICE_DESCRIPTIONW sdw; | 
|  |  | 
|  | sdw.lpDescription = SERV_dup( sd->lpDescription ); | 
|  |  | 
|  | r = ChangeServiceConfig2W( hService, dwInfoLevel, &sdw ); | 
|  |  | 
|  | SERV_free( sdw.lpDescription ); | 
|  | } | 
|  | else if (dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS) | 
|  | { | 
|  | LPSERVICE_FAILURE_ACTIONSA fa = (LPSERVICE_FAILURE_ACTIONSA) lpInfo; | 
|  | SERVICE_FAILURE_ACTIONSW faw; | 
|  |  | 
|  | faw.dwResetPeriod = fa->dwResetPeriod; | 
|  | faw.lpRebootMsg = SERV_dup( fa->lpRebootMsg ); | 
|  | faw.lpCommand = SERV_dup( fa->lpCommand ); | 
|  | faw.cActions = fa->cActions; | 
|  | faw.lpsaActions = fa->lpsaActions; | 
|  |  | 
|  | r = ChangeServiceConfig2W( hService, dwInfoLevel, &faw ); | 
|  |  | 
|  | SERV_free( faw.lpRebootMsg ); | 
|  | SERV_free( faw.lpCommand ); | 
|  | } | 
|  | else | 
|  | SetLastError( ERROR_INVALID_PARAMETER ); | 
|  |  | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * ChangeServiceConfig2W  [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI ChangeServiceConfig2W( SC_HANDLE hService, DWORD dwInfoLevel, | 
|  | LPVOID lpInfo) | 
|  | { | 
|  | HKEY hKey = ((struct sc_handle*) hService)->u.service.hkey; | 
|  |  | 
|  | if (dwInfoLevel == SERVICE_CONFIG_DESCRIPTION) | 
|  | { | 
|  | static const WCHAR szDescription[] = {'D','e','s','c','r','i','p','t','i','o','n',0}; | 
|  | LPSERVICE_DESCRIPTIONW sd = (LPSERVICE_DESCRIPTIONW)lpInfo; | 
|  | if (sd->lpDescription) | 
|  | { | 
|  | TRACE("Setting Description to %s\n",debugstr_w(sd->lpDescription)); | 
|  | if (sd->lpDescription[0] == 0) | 
|  | RegDeleteValueW(hKey,szDescription); | 
|  | else | 
|  | RegSetValueExW(hKey, szDescription, 0, REG_SZ, | 
|  | (LPVOID)sd->lpDescription, | 
|  | sizeof(WCHAR)*(strlenW(sd->lpDescription)+1)); | 
|  | } | 
|  | } | 
|  | else | 
|  | FIXME("STUB: %p %ld %p\n",hService, dwInfoLevel, lpInfo); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * QueryServiceObjectSecurity [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI QueryServiceObjectSecurity(SC_HANDLE hService, | 
|  | SECURITY_INFORMATION dwSecurityInformation, | 
|  | PSECURITY_DESCRIPTOR lpSecurityDescriptor, | 
|  | DWORD cbBufSize, LPDWORD pcbBytesNeeded) | 
|  | { | 
|  | PACL pACL = NULL; | 
|  | FIXME("%p %ld %p %lu %p\n", hService, dwSecurityInformation, | 
|  | lpSecurityDescriptor, cbBufSize, pcbBytesNeeded); | 
|  |  | 
|  |  | 
|  | InitializeSecurityDescriptor(lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION); | 
|  |  | 
|  | pACL = HeapAlloc( GetProcessHeap(), 0, sizeof(ACL) ); | 
|  | InitializeAcl(pACL, sizeof(ACL), ACL_REVISION); | 
|  | SetSecurityDescriptorDacl(lpSecurityDescriptor, TRUE, pACL, TRUE); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /****************************************************************************** | 
|  | * SetServiceObjectSecurity [ADVAPI32.@] | 
|  | */ | 
|  | BOOL WINAPI SetServiceObjectSecurity(SC_HANDLE hService, | 
|  | SECURITY_INFORMATION dwSecurityInformation, | 
|  | PSECURITY_DESCRIPTOR lpSecurityDescriptor) | 
|  | { | 
|  | FIXME("%p %ld %p\n", hService, dwSecurityInformation, lpSecurityDescriptor); | 
|  | return TRUE; | 
|  | } |