| /* |
| * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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" |
| #include "lmcons.h" |
| #include "lmserver.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; |
| union { |
| LPHANDLER_FUNCTION handler; |
| LPHANDLER_FUNCTION_EX handler_ex; |
| } handler; |
| LPVOID context; |
| SERVICE_STATUS status; |
| HANDLE thread; |
| BOOL unicode : 1; |
| BOOL extended : 1; /* uses handler_ex instead of handler? */ |
| 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, { (DWORD_PTR)(__FILE__ ": service_cs") } |
| }; |
| static CRITICAL_SECTION service_cs = { &service_cs_debug, -1, 0, 0, 0, 0 }; |
| |
| static 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 ); |
| } |
| |
| /****************************************************************************** |
| * registry access functions and data |
| */ |
| 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}; |
| static const WCHAR szDependOnService[] = { |
| 'D','e','p','e','n','d','O','n','S','e','r','v','i','c','e',0}; |
| |
| struct reg_value { |
| DWORD type; |
| DWORD size; |
| LPCWSTR name; |
| LPCVOID data; |
| }; |
| |
| static inline void service_set_value( struct reg_value *val, |
| DWORD type, LPCWSTR name, LPCVOID data, DWORD size ) |
| { |
| val->name = name; |
| val->type = type; |
| val->data = data; |
| val->size = size; |
| } |
| |
| static inline void service_set_dword( struct reg_value *val, |
| LPCWSTR name, DWORD *data ) |
| { |
| service_set_value( val, REG_DWORD, name, data, sizeof (DWORD)); |
| } |
| |
| static inline void service_set_string( struct reg_value *val, |
| LPCWSTR name, LPCWSTR string ) |
| { |
| DWORD len = (lstrlenW(string)+1) * sizeof (WCHAR); |
| service_set_value( val, REG_SZ, name, string, len ); |
| } |
| |
| static inline void service_set_multi_string( struct reg_value *val, |
| LPCWSTR name, LPCWSTR string ) |
| { |
| DWORD len = 0; |
| |
| /* determine the length of a double null terminated multi string */ |
| do { |
| len += (lstrlenW( &string[ len ] )+1); |
| } while ( string[ len++ ] ); |
| |
| len *= sizeof (WCHAR); |
| service_set_value( val, REG_MULTI_SZ, name, string, len ); |
| } |
| |
| static inline LONG service_write_values( HKEY hKey, |
| struct reg_value *val, int n ) |
| { |
| LONG r = ERROR_SUCCESS; |
| int i; |
| |
| for( i=0; i<n; i++ ) |
| { |
| r = RegSetValueExW(hKey, val[i].name, 0, val[i].type, |
| (const BYTE*)val[i].data, val[i].size ); |
| if( r != ERROR_SUCCESS ) |
| break; |
| } |
| return r; |
| } |
| |
| /****************************************************************************** |
| * 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 (!argc) |
| { |
| if (info->unicode) |
| info->proc.w(0, NULL); |
| else |
| info->proc.a(0, NULL); |
| return 0; |
| } |
| |
| 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!\n", r, count); |
| 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!\n", r, count); |
| 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 (!service->extended) |
| 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)) |
| { |
| if (service->extended && service->handler.handler_ex) |
| { |
| service->handler.handler_ex(dwControl, 0, NULL, service->context); |
| ret = ERROR_SUCCESS; |
| } |
| else if (service->handler.handler) |
| { |
| service->handler.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 for %s, error = %ld\n", |
| debugstr_w(service->name), 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.@] |
| * |
| * See StartServiceCtrlDispatcherW. |
| */ |
| 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 |
| * |
| * RETURNS |
| * Success: TRUE. |
| * Failure: FALSE. |
| */ |
| 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.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("%p %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 ); |
| SetLastError( r); |
| 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; |
| struct reg_value val[10]; |
| int n = 0; |
| |
| TRACE("%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) |
| return NULL; |
| |
| if (dp != REG_CREATED_NEW_KEY) |
| { |
| SetLastError(ERROR_SERVICE_EXISTS); |
| goto error; |
| } |
| |
| if( lpDisplayName ) |
| service_set_string( &val[n++], szDisplayName, lpDisplayName ); |
| |
| service_set_dword( &val[n++], szType, &dwServiceType ); |
| service_set_dword( &val[n++], szStart, &dwStartType ); |
| service_set_dword( &val[n++], szError, &dwErrorControl ); |
| |
| if( lpBinaryPathName ) |
| service_set_string( &val[n++], szImagePath, lpBinaryPathName ); |
| |
| if( lpLoadOrderGroup ) |
| service_set_string( &val[n++], szGroup, lpLoadOrderGroup ); |
| |
| if( lpDependencies ) |
| service_set_multi_string( &val[n++], szDependencies, lpDependencies ); |
| |
| if( lpPassword ) |
| FIXME("Don't know how to add a Password for a service.\n"); |
| |
| if( lpServiceStartName ) |
| service_set_string( &val[n++], szDependOnService, lpServiceStartName ); |
| |
| r = service_write_values( hKey, val, n ); |
| if( r != ERROR_SUCCESS ) |
| goto error; |
| |
| len = strlenW(lpServiceName)+1; |
| len = sizeof (struct sc_service) + len*sizeof(WCHAR); |
| hsvc = sc_handle_alloc( SC_HTYPE_SERVICE, len, sc_handle_destroy_service ); |
| if( !hsvc ) |
| goto error; |
| lstrcpyW( hsvc->name, lpServiceName ); |
| hsvc->hkey = hKey; |
| hsvc->scm = hscm; |
| hscm->hdr.ref_count++; |
| |
| return (SC_HANDLE) &hsvc->hdr; |
| |
| error: |
| RegCloseKey( hKey ); |
| 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((SC_HANDLE)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); |
| |
| if (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; |
| LPSTR 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, (LPBYTE)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 = (LPSTR) &lpServiceConfig[1]; |
| n = total - sizeof (QUERY_SERVICE_CONFIGA); |
| |
| sz = sizeof(str_buffer); |
| r = RegQueryValueExA( hKey, szImagePath, 0, &type, (LPBYTE)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 = p; |
| p += sz; |
| n -= sz; |
| } |
| else |
| { |
| /* FIXME: set last error */ |
| return FALSE; |
| } |
| |
| sz = n; |
| r = RegQueryValueExA( hKey, szGroup, 0, &type, (LPBYTE)p, &sz ); |
| if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) ) |
| { |
| lpServiceConfig->lpLoadOrderGroup = p; |
| p += sz; |
| n -= sz; |
| } |
| |
| sz = n; |
| r = RegQueryValueExA( hKey, szDependencies, 0, &type, (LPBYTE)p, &sz ); |
| if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) ) |
| { |
| lpServiceConfig->lpDependencies = 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) |
| { |
| 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) |
| { |
| struct reg_value val[10]; |
| struct sc_service *hsvc; |
| DWORD r = ERROR_SUCCESS; |
| HKEY hKey; |
| int n = 0; |
| |
| TRACE("%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) ); |
| |
| hsvc = sc_handle_get_handle_data(hService, SC_HTYPE_SERVICE); |
| if (!hsvc) |
| { |
| SetLastError( ERROR_INVALID_HANDLE ); |
| return FALSE; |
| } |
| hKey = hsvc->hkey; |
| |
| if( dwServiceType != SERVICE_NO_CHANGE ) |
| service_set_dword( &val[n++], szType, &dwServiceType ); |
| |
| if( dwStartType != SERVICE_NO_CHANGE ) |
| service_set_dword( &val[n++], szStart, &dwStartType ); |
| |
| if( dwErrorControl != SERVICE_NO_CHANGE ) |
| service_set_dword( &val[n++], szError, &dwErrorControl ); |
| |
| if( lpBinaryPathName ) |
| service_set_string( &val[n++], szImagePath, lpBinaryPathName ); |
| |
| if( lpLoadOrderGroup ) |
| service_set_string( &val[n++], szGroup, lpLoadOrderGroup ); |
| |
| if( lpDependencies ) |
| service_set_multi_string( &val[n++], szDependencies, lpDependencies ); |
| |
| if( lpPassword ) |
| FIXME("ignoring password\n"); |
| |
| if( lpServiceStartName ) |
| service_set_string( &val[n++], szDependOnService, lpServiceStartName ); |
| |
| r = service_write_values( hsvc->hkey, val, n ); |
| |
| return (r == ERROR_SUCCESS) ? TRUE : FALSE ; |
| } |
| |
| /****************************************************************************** |
| * 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("%p %08lx %x %x\n", hServiceStatus, dwServiceBits, |
| bSetBitsOn, bUpdateImmediately); |
| return TRUE; |
| } |
| |
| SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerExA( LPCSTR lpServiceName, |
| LPHANDLER_FUNCTION_EX lpHandlerProc, LPVOID lpContext ) |
| { |
| FIXME("%s %p %p\n", debugstr_a(lpServiceName), lpHandlerProc, lpContext); |
| return 0; |
| } |
| |
| SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerExW( LPCWSTR lpServiceName, |
| LPHANDLER_FUNCTION_EX lpHandlerProc, LPVOID lpContext ) |
| { |
| service_data *service; |
| |
| TRACE("%s %p %p\n", debugstr_w(lpServiceName), lpHandlerProc, lpContext); |
| |
| EnterCriticalSection( &service_cs ); |
| for(service = service_list; service; service = service->next) |
| if(!strcmpW(lpServiceName, service->name)) |
| break; |
| if (service) |
| { |
| service->handler.handler_ex = lpHandlerProc; |
| service->context = lpContext; |
| service->extended = TRUE; |
| } |
| LeaveCriticalSection( &service_cs ); |
| |
| return (SERVICE_STATUS_HANDLE)service; |
| } |