| /* | |
| * Win32 advapi functions | |
| * | |
| * Copyright 1995 Sven Verdoolaege | |
| * Copyright 2005 Mike McCormack | |
| * | |
| * 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 <assert.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}; | |
| typedef struct service_start_info_t | |
| { | |
| DWORD cmd; | |
| DWORD size; | |
| WCHAR str[1]; | |
| } service_start_info; | |
| #define WINESERV_STARTINFO 1 | |
| #define WINESERV_GETSTATUS 2 | |
| #define WINESERV_SENDCONTROL 3 | |
| typedef struct service_data_t | |
| { | |
| struct service_data_t *next; | |
| LPHANDLER_FUNCTION handler; | |
| SERVICE_STATUS status; | |
| 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 }; | |
| service_data *service_list; | |
| /****************************************************************************** | |
| * SC_HANDLEs | |
| */ | |
| #define MAX_SERVICE_NAME 256 | |
| typedef enum { SC_HTYPE_MANAGER, SC_HTYPE_SERVICE } SC_HANDLE_TYPE; | |
| struct sc_handle; | |
| typedef VOID (*sc_handle_destructor)(struct sc_handle *); | |
| struct sc_handle | |
| { | |
| SC_HANDLE_TYPE htype; | |
| DWORD ref_count; | |
| sc_handle_destructor destroy; | |
| }; | |
| struct sc_manager /* service control manager handle */ | |
| { | |
| struct sc_handle hdr; | |
| HKEY hkey; /* handle to services database in the registry */ | |
| }; | |
| struct sc_service /* service handle */ | |
| { | |
| struct sc_handle hdr; | |
| HKEY hkey; /* handle to service entry in the registry (under hkey) */ | |
| struct sc_manager *scm; /* pointer to SCM handle */ | |
| WCHAR name[1]; | |
| }; | |
| static void *sc_handle_alloc(SC_HANDLE_TYPE htype, DWORD size, | |
| sc_handle_destructor destroy) | |
| { | |
| struct sc_handle *hdr; | |
| hdr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ); | |
| if (hdr) | |
| { | |
| hdr->htype = htype; | |
| hdr->ref_count = 1; | |
| hdr->destroy = destroy; | |
| } | |
| TRACE("sc_handle type=%d -> %p\n", htype, hdr); | |
| return hdr; | |
| } | |
| static void *sc_handle_get_handle_data(SC_HANDLE handle, DWORD htype) | |
| { | |
| struct sc_handle *hdr = (struct sc_handle *) handle; | |
| if (!hdr) | |
| return NULL; | |
| if (hdr->htype != htype) | |
| return NULL; | |
| return hdr; | |
| } | |
| static void sc_handle_free(struct sc_handle* hdr) | |
| { | |
| if (!hdr) | |
| return; | |
| if (--hdr->ref_count) | |
| return; | |
| hdr->destroy(hdr); | |
| HeapFree(GetProcessHeap(), 0, hdr); | |
| } | |
| static void sc_handle_destroy_manager(struct sc_handle *handle) | |
| { | |
| struct sc_manager *mgr = (struct sc_manager*) handle; | |
| TRACE("destroying SC Manager %p\n", mgr); | |
| if (mgr->hkey) | |
| RegCloseKey(mgr->hkey); | |
| } | |
| static void sc_handle_destroy_service(struct sc_handle *handle) | |
| { | |
| struct sc_service *svc = (struct sc_service*) handle; | |
| TRACE("destroying service %p\n", svc); | |
| if (svc->hkey) | |
| RegCloseKey(svc->hkey); | |
| svc->hkey = NULL; | |
| sc_handle_free(&svc->scm->hdr); | |
| svc->scm = NULL; | |
| } | |
| /****************************************************************************** | |
| * String management functions | |
| */ | |
| 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 ); | |
| } | |
| /****************************************************************************** | |
| * Service IPC functions | |
| */ | |
| static LPWSTR service_get_pipe_name(LPWSTR service) | |
| { | |
| static const WCHAR prefix[] = { '\\','\\','.','\\','p','i','p','e','\\', | |
| '_','_','w','i','n','e','s','e','r','v','i','c','e','_',0}; | |
| LPWSTR name; | |
| DWORD len; | |
| len = sizeof prefix + strlenW(service)*sizeof(WCHAR); | |
| name = HeapAlloc(GetProcessHeap(), 0, len); | |
| strcpyW(name, prefix); | |
| strcatW(name, service); | |
| return name; | |
| } | |
| static HANDLE service_open_pipe(LPWSTR service) | |
| { | |
| LPWSTR szPipe = service_get_pipe_name( service ); | |
| HANDLE handle = INVALID_HANDLE_VALUE; | |
| do { | |
| handle = CreateFileW(szPipe, GENERIC_READ|GENERIC_WRITE, | |
| 0, NULL, OPEN_ALWAYS, 0, NULL); | |
| if (handle != INVALID_HANDLE_VALUE) | |
| break; | |
| if (GetLastError() != ERROR_PIPE_BUSY) | |
| break; | |
| } while (WaitNamedPipeW(szPipe, NMPWAIT_WAIT_FOREVER)); | |
| SERV_free(szPipe); | |
| return handle; | |
| } | |
| /****************************************************************************** | |
| * service_get_event_handle | |
| */ | |
| static HANDLE service_get_event_handle(LPWSTR service) | |
| { | |
| static const WCHAR prefix[] = { | |
| '_','_','w','i','n','e','s','e','r','v','i','c','e','_',0}; | |
| LPWSTR name; | |
| DWORD len; | |
| HANDLE handle; | |
| len = sizeof prefix + strlenW(service)*sizeof(WCHAR); | |
| name = HeapAlloc(GetProcessHeap(), 0, len); | |
| strcpyW(name, prefix); | |
| strcatW(name, service); | |
| handle = CreateEventW(NULL, TRUE, FALSE, name); | |
| SERV_free(name); | |
| return handle; | |
| } | |
| /****************************************************************************** | |
| * service_thread | |
| * | |
| * Call into the main service routine provided by StartServiceCtrlDispatcher. | |
| */ | |
| static DWORD WINAPI service_thread(LPVOID arg) | |
| { | |
| service_data *info = arg; | |
| LPWSTR str = info->args; | |
| DWORD argc = 0, len = 0; | |
| TRACE("%p\n", arg); | |
| 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 | |
| { | |
| 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; | |
| } | |
| /****************************************************************************** | |
| * service_handle_start | |
| */ | |
| static BOOL service_handle_start(HANDLE pipe, service_data *service, DWORD count) | |
| { | |
| DWORD read = 0, result = 0; | |
| LPWSTR args; | |
| BOOL r; | |
| TRACE("%p %p %ld\n", pipe, service, count); | |
| args = HeapAlloc(GetProcessHeap(), 0, count*sizeof(WCHAR)); | |
| r = ReadFile(pipe, args, count*sizeof(WCHAR), &read, NULL); | |
| if (!r || count!=read/sizeof(WCHAR) || args[count-1]) | |
| { | |
| ERR("pipe read failed r = %d count = %ld/%ld args[n-1]=%s\n", | |
| r, count, read/sizeof(WCHAR), debugstr_wn(args, count)); | |
| goto end; | |
| } | |
| if (service->thread) | |
| { | |
| ERR("service is not stopped\n"); | |
| goto end; | |
| } | |
| if (service->args) | |
| SERV_free(service->args); | |
| service->args = args; | |
| args = NULL; | |
| service->thread = CreateThread( NULL, 0, service_thread, | |
| service, 0, NULL ); | |
| end: | |
| HeapFree(GetProcessHeap(), 0, args); | |
| WriteFile( pipe, &result, sizeof result, &read, NULL ); | |
| return TRUE; | |
| } | |
| /****************************************************************************** | |
| * 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; | |
| TRACE("%p %p %ld\n", pipe, argv, argc); | |
| /* calculate how much space do we need to send the startup info */ | |
| len = 1; | |
| for (i=0; i<argc; i++) | |
| len += strlenW(argv[i])+1; | |
| ssi = HeapAlloc(GetProcessHeap(),0,sizeof *ssi + (len-1)*sizeof(WCHAR)); | |
| ssi->cmd = WINESERV_STARTINFO; | |
| ssi->size = len; | |
| /* copy service args into a single buffer*/ | |
| p = &ssi->str[0]; | |
| for (i=0; i<argc; i++) | |
| { | |
| strcpyW(p, argv[i]); | |
| p += strlenW(p) + 1; | |
| } | |
| *p=0; | |
| r = WriteFile(pipe, ssi, sizeof *ssi + len*sizeof(WCHAR), &count, NULL); | |
| if (r) | |
| r = ReadFile(pipe, &result, sizeof result, &count, NULL); | |
| HeapFree(GetProcessHeap(),0,ssi); | |
| return r; | |
| } | |
| /****************************************************************************** | |
| * service_handle_get_status | |
| */ | |
| static BOOL service_handle_get_status(HANDLE pipe, service_data *service) | |
| { | |
| DWORD count = 0; | |
| TRACE("\n"); | |
| return WriteFile(pipe, &service->status, | |
| sizeof service->status, &count, NULL); | |
| } | |
| /****************************************************************************** | |
| * 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; | |
| } | |
| /****************************************************************************** | |
| * service_send_control | |
| */ | |
| static BOOL service_send_control(HANDLE pipe, DWORD dwControl, DWORD *result) | |
| { | |
| DWORD cmd[2], count = 0; | |
| BOOL r; | |
| cmd[0] = WINESERV_SENDCONTROL; | |
| cmd[1] = dwControl; | |
| r = WriteFile(pipe, cmd, sizeof cmd, &count, NULL); | |
| if (!r || count != sizeof cmd) | |
| { | |
| ERR("service protocol error - failed to write pipe!\n"); | |
| return r; | |
| } | |
| r = ReadFile(pipe, result, sizeof *result, &count, NULL); | |
| if (!r || count != sizeof *result) | |
| ERR("service protocol error - failed to read pipe " | |
| "r = %d count = %ld/%d!\n", r, count, sizeof *result); | |
| return r; | |
| } | |
| /****************************************************************************** | |
| * service_accepts_control | |
| */ | |
| static BOOL service_accepts_control(service_data *service, DWORD dwControl) | |
| { | |
| DWORD a = service->status.dwControlsAccepted; | |
| switch (dwControl) | |
| { | |
| case SERVICE_CONTROL_INTERROGATE: | |
| return TRUE; | |
| case SERVICE_CONTROL_STOP: | |
| if (a&SERVICE_ACCEPT_STOP) | |
| return TRUE; | |
| break; | |
| case SERVICE_CONTROL_SHUTDOWN: | |
| if (a&SERVICE_ACCEPT_SHUTDOWN) | |
| return TRUE; | |
| break; | |
| case SERVICE_CONTROL_PAUSE: | |
| case SERVICE_CONTROL_CONTINUE: | |
| if (a&SERVICE_ACCEPT_PAUSE_CONTINUE) | |
| return TRUE; | |
| break; | |
| case SERVICE_CONTROL_PARAMCHANGE: | |
| if (a&SERVICE_ACCEPT_PARAMCHANGE) | |
| return TRUE; | |
| break; | |
| case SERVICE_CONTROL_NETBINDADD: | |
| case SERVICE_CONTROL_NETBINDREMOVE: | |
| case SERVICE_CONTROL_NETBINDENABLE: | |
| case SERVICE_CONTROL_NETBINDDISABLE: | |
| if (a&SERVICE_ACCEPT_NETBINDCHANGE) | |
| return TRUE; | |
| } | |
| if (1) /* (!service->handlerex) */ | |
| return FALSE; | |
| switch (dwControl) | |
| { | |
| case SERVICE_CONTROL_HARDWAREPROFILECHANGE: | |
| if (a&SERVICE_ACCEPT_HARDWAREPROFILECHANGE) | |
| return TRUE; | |
| break; | |
| case SERVICE_CONTROL_POWEREVENT: | |
| if (a&SERVICE_ACCEPT_POWEREVENT) | |
| return TRUE; | |
| break; | |
| case SERVICE_CONTROL_SESSIONCHANGE: | |
| if (a&SERVICE_ACCEPT_SESSIONCHANGE) | |
| return TRUE; | |
| break; | |
| } | |
| return FALSE; | |
| } | |
| /****************************************************************************** | |
| * service_handle_control | |
| */ | |
| static BOOL service_handle_control(HANDLE pipe, service_data *service, | |
| DWORD dwControl) | |
| { | |
| DWORD count, ret = ERROR_INVALID_SERVICE_CONTROL; | |
| TRACE("received control %ld\n", dwControl); | |
| if (service_accepts_control(service, dwControl) && service->handler) | |
| { | |
| service->handler(dwControl); | |
| ret = ERROR_SUCCESS; | |
| } | |
| return WriteFile(pipe, &ret, sizeof ret, &count, NULL); | |
| } | |
| /****************************************************************************** | |
| * service_reap_thread | |
| */ | |
| static DWORD service_reap_thread(service_data *service) | |
| { | |
| DWORD exitcode = 0; | |
| if (!service->thread) | |
| return 0; | |
| GetExitCodeThread(service->thread, &exitcode); | |
| if (exitcode!=STILL_ACTIVE) | |
| { | |
| CloseHandle(service->thread); | |
| service->thread = 0; | |
| } | |
| return exitcode; | |
| } | |
| /****************************************************************************** | |
| * service_control_dispatcher | |
| */ | |
| static DWORD WINAPI service_control_dispatcher(LPVOID arg) | |
| { | |
| service_data *service = arg; | |
| LPWSTR name; | |
| HANDLE pipe, event; | |
| 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); | |
| if (pipe==INVALID_HANDLE_VALUE) | |
| { | |
| ERR("failed to create pipe, error = %ld\n", GetLastError()); | |
| return 0; | |
| } | |
| /* dispatcher loop */ | |
| while (1) | |
| { | |
| BOOL r; | |
| DWORD count, req[2] = {0,0}; | |
| r = ConnectNamedPipe(pipe, NULL); | |
| if (!r && GetLastError() != ERROR_PIPE_CONNECTED) | |
| { | |
| ERR("pipe connect failed\n"); | |
| break; | |
| } | |
| r = ReadFile( pipe, &req, sizeof req, &count, NULL ); | |
| if (!r || count!=sizeof req) | |
| { | |
| ERR("pipe read failed\n"); | |
| break; | |
| } | |
| service_reap_thread(service); | |
| /* 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; | |
| case WINESERV_SENDCONTROL: | |
| service_handle_control(pipe, service, req[1]); | |
| break; | |
| default: | |
| ERR("received invalid command %ld length %ld\n", req[0], req[1]); | |
| } | |
| FlushFileBuffers(pipe); | |
| DisconnectNamedPipe(pipe); | |
| } | |
| CloseHandle(pipe); | |
| return 1; | |
| } | |
| /****************************************************************************** | |
| * service_run_threads | |
| */ | |
| static BOOL service_run_threads(void) | |
| { | |
| service_data *service; | |
| DWORD count = 0, n = 0; | |
| HANDLE *handles; | |
| EnterCriticalSection( &service_cs ); | |
| /* count how many services there are */ | |
| for (service = service_list; service; service = service->next) | |
| count++; | |
| TRACE("starting %ld pipe listener threads\n", count); | |
| handles = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE)*count); | |
| for (n=0, service = service_list; service; service = service->next, n++) | |
| handles[n] = CreateThread( NULL, 0, service_control_dispatcher, | |
| service, 0, NULL ); | |
| assert(n==count); | |
| LeaveCriticalSection( &service_cs ); | |
| /* wait for all the threads to pack up and exit */ | |
| WaitForMultipleObjectsEx(count, handles, TRUE, INFINITE, FALSE); | |
| HeapFree(GetProcessHeap(), 0, handles); | |
| return TRUE; | |
| } | |
| /****************************************************************************** | |
| * StartServiceCtrlDispatcherA [ADVAPI32.@] | |
| * | |
| * Connects a process containing one or more services to the service control | |
| * manager. | |
| * | |
| * PARAMS | |
| * servent [I] A list of the service names and service procedures | |
| */ | |
| BOOL WINAPI StartServiceCtrlDispatcherA( LPSERVICE_TABLE_ENTRYA servent ) | |
| { | |
| service_data *info; | |
| DWORD sz, len; | |
| BOOL ret = TRUE; | |
| TRACE("%p\n", servent); | |
| EnterCriticalSection( &service_cs ); | |
| while (servent->lpServiceName) | |
| { | |
| 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++; | |
| } | |
| LeaveCriticalSection( &service_cs ); | |
| service_run_threads(); | |
| return ret; | |
| } | |
| /****************************************************************************** | |
| * StartServiceCtrlDispatcherW [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 StartServiceCtrlDispatcherW( LPSERVICE_TABLE_ENTRYW servent ) | |
| { | |
| 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; | |
| } | |
| /****************************************************************************** | |
| * LockServiceDatabase [ADVAPI32.@] | |
| */ | |
| SC_LOCK WINAPI LockServiceDatabase (SC_HANDLE hSCManager) | |
| { | |
| HANDLE ret; | |
| TRACE("%p\n",hSCManager); | |
| ret = CreateSemaphoreW( NULL, 1, 1, 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 (SC_LOCK ScLock) | |
| { | |
| TRACE("%p\n",ScLock); | |
| return CloseHandle( 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 ) | |
| { | |
| 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 ); | |
| return (SERVICE_STATUS_HANDLE)service; | |
| } | |
| /****************************************************************************** | |
| * SetServiceStatus [ADVAPI32.@] | |
| * | |
| * PARAMS | |
| * hService [] | |
| * lpStatus [] | |
| */ | |
| BOOL WINAPI | |
| SetServiceStatus( SERVICE_STATUS_HANDLE hService, LPSERVICE_STATUS lpStatus ) | |
| { | |
| service_data *service; | |
| BOOL r = 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.@] | |
| * | |
| * 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_manager *manager; | |
| 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; | |
| } | |
| } | |
| manager = sc_handle_alloc( SC_HTYPE_MANAGER, sizeof (struct sc_manager), | |
| sc_handle_destroy_manager ); | |
| if (!manager) | |
| return NULL; | |
| r = RegConnectRegistryW(lpMachineName,HKEY_LOCAL_MACHINE,&hReg); | |
| if (r!=ERROR_SUCCESS) | |
| goto error; | |
| r = RegOpenKeyExW(hReg, szServiceManagerKey, | |
| 0, KEY_ALL_ACCESS, &manager->hkey); | |
| RegCloseKey( hReg ); | |
| if (r!=ERROR_SUCCESS) | |
| goto error; | |
| TRACE("returning %p\n", manager); | |
| return (SC_HANDLE) &manager->hdr; | |
| error: | |
| sc_handle_free( &manager->hdr ); | |
| 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_service *hsvc; | |
| BOOL ret = FALSE; | |
| HANDLE handle; | |
| TRACE("%p %ld %p\n", hService, dwControl, lpServiceStatus); | |
| hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | |
| if (!hsvc) | |
| { | |
| SetLastError( ERROR_INVALID_HANDLE ); | |
| return FALSE; | |
| } | |
| ret = QueryServiceStatus(hService, lpServiceStatus); | |
| if (ret) | |
| { | |
| ERR("failed to query service status\n"); | |
| SetLastError(ERROR_SERVICE_NOT_ACTIVE); | |
| return FALSE; | |
| } | |
| switch (lpServiceStatus->dwCurrentState) | |
| { | |
| case SERVICE_STOPPED: | |
| SetLastError(ERROR_SERVICE_NOT_ACTIVE); | |
| return FALSE; | |
| case SERVICE_START_PENDING: | |
| if (dwControl==SERVICE_CONTROL_STOP) | |
| break; | |
| /* fall thru */ | |
| case SERVICE_STOP_PENDING: | |
| SetLastError(ERROR_SERVICE_CANNOT_ACCEPT_CTRL); | |
| return FALSE; | |
| } | |
| handle = service_open_pipe(hsvc->name); | |
| if (handle!=INVALID_HANDLE_VALUE) | |
| { | |
| DWORD result = ERROR_SUCCESS; | |
| ret = service_send_control(handle, dwControl, &result); | |
| CloseHandle(handle); | |
| if (result!=ERROR_SUCCESS) | |
| { | |
| SetLastError(result); | |
| ret = FALSE; | |
| } | |
| } | |
| 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); | |
| sc_handle_free( (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_manager *hscm; | |
| struct sc_service *hsvc; | |
| HKEY hKey; | |
| long r; | |
| DWORD len; | |
| TRACE("%p %s %ld\n", hSCManager, debugstr_w(lpServiceName), dwDesiredAccess); | |
| if (!lpServiceName) | |
| { | |
| SetLastError(ERROR_INVALID_ADDRESS); | |
| return NULL; | |
| } | |
| hscm = sc_handle_get_handle_data( hSCManager, SC_HTYPE_MANAGER ); | |
| if (!hscm) | |
| { | |
| SetLastError( ERROR_INVALID_HANDLE ); | |
| return FALSE; | |
| } | |
| r = RegOpenKeyExW( hscm->hkey, lpServiceName, 0, KEY_ALL_ACCESS, &hKey ); | |
| if (r!=ERROR_SUCCESS) | |
| { | |
| SetLastError( ERROR_SERVICE_DOES_NOT_EXIST ); | |
| return NULL; | |
| } | |
| len = strlenW(lpServiceName)+1; | |
| hsvc = sc_handle_alloc( SC_HTYPE_SERVICE, | |
| sizeof (struct sc_service) + len*sizeof(WCHAR), | |
| sc_handle_destroy_service ); | |
| if (!hsvc) | |
| return NULL; | |
| strcpyW( hsvc->name, lpServiceName ); | |
| hsvc->hkey = hKey; | |
| /* add reference to SCM handle */ | |
| hscm->hdr.ref_count++; | |
| hsvc->scm = hscm; | |
| TRACE("returning %p\n",hsvc); | |
| return (SC_HANDLE) &hsvc->hdr; | |
| } | |
| /****************************************************************************** | |
| * 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_manager *hscm; | |
| struct sc_service *hsvc = NULL; | |
| HKEY hKey; | |
| LONG r; | |
| DWORD dp, len; | |
| 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)); | |
| hscm = sc_handle_get_handle_data( hSCManager, SC_HTYPE_MANAGER ); | |
| if (!hscm) | |
| { | |
| SetLastError( ERROR_INVALID_HANDLE ); | |
| return NULL; | |
| } | |
| r = RegCreateKeyExW(hscm->hkey, lpServiceName, 0, NULL, | |
| REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dp); | |
| if (r!=ERROR_SUCCESS) | |
| goto error; | |
| 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"); | |
| } | |
| len = strlenW(lpServiceName)+1; | |
| hsvc = sc_handle_alloc( SC_HTYPE_SERVICE, | |
| sizeof (struct sc_service) + len*sizeof(WCHAR), | |
| sc_handle_destroy_service ); | |
| if (!hsvc) | |
| return NULL; | |
| strcpyW( hsvc->name, lpServiceName ); | |
| hsvc->hkey = hKey; | |
| hsvc->scm = hscm; | |
| hscm->hdr.ref_count++; | |
| return (SC_HANDLE) &hsvc->hdr; | |
| error: | |
| if (hsvc) | |
| sc_handle_free( &hsvc->hdr ); | |
| 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_service *hsvc; | |
| HKEY hKey; | |
| WCHAR valname[MAX_PATH+1]; | |
| INT index = 0; | |
| LONG rc; | |
| DWORD size; | |
| hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | |
| if (!hsvc) | |
| { | |
| SetLastError( ERROR_INVALID_HANDLE ); | |
| return FALSE; | |
| } | |
| hKey = hsvc->hkey; | |
| 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->hkey = NULL; | |
| /* delete the key */ | |
| RegDeleteKeyW(hsvc->scm->hkey, hsvc->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; | |
| BOOL r; | |
| 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]); | |
| r = StartServiceW(hService, dwNumServiceArgs, (LPCWSTR *)lpwstr); | |
| if (dwNumServiceArgs) | |
| { | |
| for(i=0; i<dwNumServiceArgs; i++) | |
| SERV_free(lpwstr[i]); | |
| HeapFree(GetProcessHeap(), 0, lpwstr); | |
| } | |
| return r; | |
| } | |
| /****************************************************************************** | |
| * service_start_process [INTERNAL] | |
| */ | |
| static DWORD service_start_process(struct sc_service *hsvc) | |
| { | |
| 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; | |
| /* 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; | |
| } | |
| CloseHandle( pi.hThread ); | |
| CloseHandle( pi.hProcess ); | |
| } | |
| CloseHandle( handles[0] ); | |
| HeapFree(GetProcessHeap(),0,path); | |
| return r; | |
| } | |
| static BOOL service_wait_for_startup(SC_HANDLE hService) | |
| { | |
| DWORD i; | |
| SERVICE_STATUS status; | |
| BOOL r = FALSE; | |
| TRACE("%p\n", hService); | |
| for (i=0; i<30; i++) | |
| { | |
| status.dwCurrentState = 0; | |
| r = QueryServiceStatus(hService, &status); | |
| if (!r) | |
| break; | |
| if (status.dwCurrentState == SERVICE_RUNNING) | |
| { | |
| TRACE("Service started successfully\n"); | |
| break; | |
| } | |
| r = FALSE; | |
| Sleep(1000); | |
| } | |
| return r; | |
| } | |
| /****************************************************************************** | |
| * 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) | |
| { | |
| SetLastError(ERROR_INVALID_HANDLE); | |
| return r; | |
| } | |
| hLock = LockServiceDatabase(hsvc->scm); | |
| if (!hLock) | |
| return r; | |
| handle = service_open_pipe(hsvc->name); | |
| if (handle==INVALID_HANDLE_VALUE) | |
| { | |
| /* start the service process */ | |
| if (service_start_process(hsvc)) | |
| handle = service_open_pipe(hsvc->name); | |
| } | |
| if (handle != INVALID_HANDLE_VALUE) | |
| { | |
| service_send_start_message(handle, lpServiceArgVectors, dwNumServiceArgs); | |
| CloseHandle(handle); | |
| r = TRUE; | |
| } | |
| UnlockServiceDatabase( hLock ); | |
| TRACE("returning %d\n", r); | |
| service_wait_for_startup(hService); | |
| return r; | |
| } | |
| /****************************************************************************** | |
| * QueryServiceStatus [ADVAPI32.@] | |
| * | |
| * PARAMS | |
| * hService [] | |
| * lpservicestatus [] | |
| * | |
| */ | |
| BOOL WINAPI QueryServiceStatus(SC_HANDLE hService, | |
| LPSERVICE_STATUS lpservicestatus) | |
| { | |
| struct sc_service *hsvc; | |
| DWORD size, type, val; | |
| HANDLE pipe; | |
| LONG r; | |
| TRACE("%p %p\n", hService, lpservicestatus); | |
| hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | |
| if (!hsvc) | |
| { | |
| SetLastError( ERROR_INVALID_HANDLE ); | |
| return FALSE; | |
| } | |
| pipe = service_open_pipe(hsvc->name); | |
| if (pipe != INVALID_HANDLE_VALUE) | |
| { | |
| r = service_get_status(pipe, lpservicestatus); | |
| CloseHandle(pipe); | |
| if (r) | |
| return TRUE; | |
| } | |
| TRACE("Failed to read service status\n"); | |
| /* read the service type from the registry */ | |
| size = sizeof(val); | |
| r = RegQueryValueExA(hsvc->hkey, "Type", NULL, &type, (LPBYTE)&val, &size); | |
| if(r!=ERROR_SUCCESS || type!=REG_DWORD) | |
| val = 0; | |
| lpservicestatus->dwServiceType = val; | |
| lpservicestatus->dwCurrentState = SERVICE_STOPPED; /* stopped */ | |
| lpservicestatus->dwControlsAccepted = 0; | |
| lpservicestatus->dwWin32ExitCode = ERROR_SERVICE_NEVER_STARTED; | |
| 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"; | |
| struct sc_service *hsvc; | |
| HKEY 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); | |
| hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | |
| if (!hsvc) | |
| { | |
| SetLastError( ERROR_INVALID_HANDLE ); | |
| return FALSE; | |
| } | |
| hKey = hsvc->hkey; | |
| /* 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; | |
| *pcbBytesNeeded = total; | |
| /* if there's not enough memory, return an error */ | |
| if( total > cbBufSize ) | |
| { | |
| SetLastError( ERROR_INSUFFICIENT_BUFFER ); | |
| return FALSE; | |
| } | |
| 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}; | |
| WCHAR str_buffer[ MAX_PATH ]; | |
| LONG r; | |
| DWORD type, val, sz, total, n; | |
| LPBYTE p; | |
| HKEY hKey; | |
| struct sc_service *hsvc; | |
| TRACE("%p %p %ld %p\n", hService, lpServiceConfig, | |
| cbBufSize, pcbBytesNeeded); | |
| hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | |
| if (!hsvc) | |
| { | |
| SetLastError( ERROR_INVALID_HANDLE ); | |
| return FALSE; | |
| } | |
| hKey = hsvc->hkey; | |
| /* 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; | |
| else | |
| total += sizeof(WCHAR); | |
| 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; | |
| *pcbBytesNeeded = total; | |
| /* if there's not enough memory, return an error */ | |
| if( total > cbBufSize ) | |
| { | |
| SetLastError( ERROR_INSUFFICIENT_BUFFER ); | |
| return FALSE; | |
| } | |
| 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 ); | |
| lpServiceConfig->lpDependencies = (LPWSTR) p; | |
| if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) ) | |
| { | |
| p += sz; | |
| n -= sz; | |
| } | |
| else | |
| { | |
| *(WCHAR *) p = 0; | |
| p += sizeof(WCHAR); | |
| n -= sizeof(WCHAR); | |
| } | |
| 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; | |
| } | |
| /****************************************************************************** | |
| * 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; | |
| } | |
| /****************************************************************************** | |
| * GetServiceKeyNameA [ADVAPI32.@] | |
| */ | |
| BOOL WINAPI GetServiceKeyNameA( SC_HANDLE hSCManager, LPCSTR lpDisplayName, | |
| LPSTR lpServiceName, LPDWORD lpcchBuffer ) | |
| { | |
| FIXME("%p %s %p %p\n", hSCManager, debugstr_a(lpDisplayName), lpServiceName, lpcchBuffer); | |
| return FALSE; | |
| } | |
| /****************************************************************************** | |
| * GetServiceKeyNameW [ADVAPI32.@] | |
| */ | |
| BOOL WINAPI GetServiceKeyNameW( SC_HANDLE hSCManager, LPCWSTR lpDisplayName, | |
| LPWSTR lpServiceName, LPDWORD lpcchBuffer ) | |
| { | |
| FIXME("%p %s %p %p\n", hSCManager, debugstr_w(lpDisplayName), lpServiceName, lpcchBuffer); | |
| return FALSE; | |
| } | |
| /****************************************************************************** | |
| * QueryServiceLockStatusA [ADVAPI32.@] | |
| */ | |
| BOOL WINAPI QueryServiceLockStatusA( SC_HANDLE hSCManager, | |
| LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus, | |
| DWORD cbBufSize, LPDWORD pcbBytesNeeded) | |
| { | |
| FIXME("%p %p %08lx %p\n", hSCManager, lpLockStatus, cbBufSize, pcbBytesNeeded); | |
| return FALSE; | |
| } | |
| /****************************************************************************** | |
| * QueryServiceLockStatusW [ADVAPI32.@] | |
| */ | |
| BOOL WINAPI QueryServiceLockStatusW( SC_HANDLE hSCManager, | |
| LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus, | |
| DWORD cbBufSize, LPDWORD pcbBytesNeeded) | |
| { | |
| FIXME("%p %p %08lx %p\n", hSCManager, lpLockStatus, cbBufSize, pcbBytesNeeded); | |
| return FALSE; | |
| } | |
| /****************************************************************************** | |
| * 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_service *hsvc; | |
| hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); | |
| if (!hsvc) | |
| { | |
| SetLastError( ERROR_INVALID_HANDLE ); | |
| return FALSE; | |
| } | |
| hKey = hsvc->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; | |
| } | |
| /****************************************************************************** | |
| * SetServiceBits [ADVAPI32.@] | |
| */ | |
| BOOL WINAPI SetServiceBits( SERVICE_STATUS_HANDLE hServiceStatus, | |
| DWORD dwServiceBits, | |
| BOOL bSetBitsOn, | |
| BOOL bUpdateImmediately) | |
| { | |
| FIXME("%08lx %08lx %x %x\n", hServiceStatus, dwServiceBits, | |
| bSetBitsOn, bUpdateImmediately); | |
| return TRUE; | |
| } |