services: Add support for service status change notifications.
Signed-off-by: Andrew Eikum <aeikum@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
7902e43245
commit
73e8d0e6a5
|
@ -59,7 +59,8 @@ typedef enum
|
||||||
{
|
{
|
||||||
SC_HTYPE_DONT_CARE = 0,
|
SC_HTYPE_DONT_CARE = 0,
|
||||||
SC_HTYPE_MANAGER,
|
SC_HTYPE_MANAGER,
|
||||||
SC_HTYPE_SERVICE
|
SC_HTYPE_SERVICE,
|
||||||
|
SC_HTYPE_NOTIFY
|
||||||
} SC_HANDLE_TYPE;
|
} SC_HANDLE_TYPE;
|
||||||
|
|
||||||
struct sc_handle
|
struct sc_handle
|
||||||
|
@ -80,6 +81,32 @@ struct sc_service_handle /* service handle */
|
||||||
struct service_entry *service_entry;
|
struct service_entry *service_entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sc_notify_handle
|
||||||
|
{
|
||||||
|
struct sc_handle hdr;
|
||||||
|
struct sc_service_handle *service;
|
||||||
|
HANDLE event;
|
||||||
|
DWORD notify_mask;
|
||||||
|
LONG ref;
|
||||||
|
SC_RPC_NOTIFY_PARAMS_LIST *params_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void sc_notify_retain(struct sc_notify_handle *notify)
|
||||||
|
{
|
||||||
|
InterlockedIncrement(¬ify->ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sc_notify_release(struct sc_notify_handle *notify)
|
||||||
|
{
|
||||||
|
ULONG r = InterlockedDecrement(¬ify->ref);
|
||||||
|
if (r == 0)
|
||||||
|
{
|
||||||
|
CloseHandle(notify->event);
|
||||||
|
HeapFree(GetProcessHeap(), 0, notify->params_list);
|
||||||
|
HeapFree(GetProcessHeap(), 0, notify);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct sc_lock
|
struct sc_lock
|
||||||
{
|
{
|
||||||
struct scmdatabase *db;
|
struct scmdatabase *db;
|
||||||
|
@ -227,6 +254,15 @@ static DWORD validate_service_handle(SC_RPC_HANDLE handle, DWORD needed_access,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DWORD validate_notify_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_notify_handle **notify)
|
||||||
|
{
|
||||||
|
struct sc_handle *hdr;
|
||||||
|
DWORD err = validate_context_handle(handle, SC_HTYPE_NOTIFY, needed_access, &hdr);
|
||||||
|
if (err == ERROR_SUCCESS)
|
||||||
|
*notify = (struct sc_notify_handle *)hdr;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
DWORD __cdecl svcctl_OpenSCManagerW(
|
DWORD __cdecl svcctl_OpenSCManagerW(
|
||||||
MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */
|
MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */
|
||||||
LPCWSTR DatabaseName,
|
LPCWSTR DatabaseName,
|
||||||
|
@ -274,6 +310,15 @@ static void SC_RPC_HANDLE_destroy(SC_RPC_HANDLE handle)
|
||||||
case SC_HTYPE_SERVICE:
|
case SC_HTYPE_SERVICE:
|
||||||
{
|
{
|
||||||
struct sc_service_handle *service = (struct sc_service_handle *)hdr;
|
struct sc_service_handle *service = (struct sc_service_handle *)hdr;
|
||||||
|
service_lock(service->service_entry);
|
||||||
|
if (service->service_entry->notify &&
|
||||||
|
service->service_entry->notify->service == service)
|
||||||
|
{
|
||||||
|
SetEvent(service->service_entry->notify->event);
|
||||||
|
sc_notify_release(service->service_entry->notify);
|
||||||
|
service->service_entry->notify = NULL;
|
||||||
|
}
|
||||||
|
service_unlock(service->service_entry);
|
||||||
release_service(service->service_entry);
|
release_service(service->service_entry);
|
||||||
HeapFree(GetProcessHeap(), 0, service);
|
HeapFree(GetProcessHeap(), 0, service);
|
||||||
break;
|
break;
|
||||||
|
@ -776,13 +821,42 @@ DWORD __cdecl svcctl_ChangeServiceConfigW(
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void fill_notify(struct sc_notify_handle *notify)
|
||||||
|
{
|
||||||
|
SC_RPC_NOTIFY_PARAMS_LIST *list;
|
||||||
|
SERVICE_NOTIFY_STATUS_CHANGE_PARAMS_2 *cparams;
|
||||||
|
|
||||||
|
list = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
|
||||||
|
sizeof(SC_RPC_NOTIFY_PARAMS_LIST) + sizeof(SERVICE_NOTIFY_STATUS_CHANGE_PARAMS_2));
|
||||||
|
if (!list)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cparams = (SERVICE_NOTIFY_STATUS_CHANGE_PARAMS_2 *)(list + 1);
|
||||||
|
|
||||||
|
cparams->dwNotifyMask = notify->notify_mask;
|
||||||
|
memcpy(&cparams->ServiceStatus, ¬ify->service->service_entry->status,
|
||||||
|
sizeof(SERVICE_STATUS_PROCESS));
|
||||||
|
cparams->dwNotificationStatus = ERROR_SUCCESS;
|
||||||
|
cparams->dwNotificationTriggered = 1 << (cparams->ServiceStatus.dwCurrentState - SERVICE_STOPPED);
|
||||||
|
cparams->pszServiceNames = NULL;
|
||||||
|
|
||||||
|
list->cElements = 1;
|
||||||
|
|
||||||
|
list->NotifyParamsArray[0].dwInfoLevel = 2;
|
||||||
|
list->NotifyParamsArray[0].u.params = cparams;
|
||||||
|
|
||||||
|
InterlockedExchangePointer((void**)¬ify->params_list, list);
|
||||||
|
|
||||||
|
SetEvent(notify->event);
|
||||||
|
}
|
||||||
|
|
||||||
DWORD __cdecl svcctl_SetServiceStatus(
|
DWORD __cdecl svcctl_SetServiceStatus(
|
||||||
SC_RPC_HANDLE hServiceStatus,
|
SC_RPC_HANDLE hServiceStatus,
|
||||||
LPSERVICE_STATUS lpServiceStatus)
|
LPSERVICE_STATUS lpServiceStatus)
|
||||||
{
|
{
|
||||||
struct sc_service_handle *service;
|
struct sc_service_handle *service;
|
||||||
struct process_entry *process;
|
struct process_entry *process;
|
||||||
DWORD err;
|
DWORD err, mask;
|
||||||
|
|
||||||
WINE_TRACE("(%p, %p)\n", hServiceStatus, lpServiceStatus);
|
WINE_TRACE("(%p, %p)\n", hServiceStatus, lpServiceStatus);
|
||||||
|
|
||||||
|
@ -807,6 +881,19 @@ DWORD __cdecl svcctl_SetServiceStatus(
|
||||||
release_process(process);
|
release_process(process);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mask = 1 << (service->service_entry->status.dwCurrentState - SERVICE_STOPPED);
|
||||||
|
if (service->service_entry->notify &&
|
||||||
|
(service->service_entry->notify->notify_mask & mask))
|
||||||
|
{
|
||||||
|
struct sc_notify_handle *notify = service->service_entry->notify;
|
||||||
|
fill_notify(notify);
|
||||||
|
service->service_entry->notify = NULL;
|
||||||
|
sc_notify_release(notify);
|
||||||
|
service->service_entry->status_notified = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
service->service_entry->status_notified = FALSE;
|
||||||
|
|
||||||
service_unlock(service->service_entry);
|
service_unlock(service->service_entry);
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
|
@ -1598,31 +1685,142 @@ DWORD __cdecl svcctl_unknown46(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD __cdecl svcctl_NotifyServiceStatusChange(
|
DWORD __cdecl svcctl_NotifyServiceStatusChange(
|
||||||
SC_RPC_HANDLE service,
|
SC_RPC_HANDLE handle,
|
||||||
SC_RPC_NOTIFY_PARAMS params,
|
SC_RPC_NOTIFY_PARAMS params,
|
||||||
GUID *clientprocessguid,
|
GUID *clientprocessguid,
|
||||||
GUID *scmprocessguid,
|
GUID *scmprocessguid,
|
||||||
BOOL *createremotequeue,
|
BOOL *createremotequeue,
|
||||||
SC_NOTIFY_RPC_HANDLE *notify)
|
SC_NOTIFY_RPC_HANDLE *hNotify)
|
||||||
{
|
{
|
||||||
WINE_FIXME("\n");
|
DWORD err, mask;
|
||||||
return ERROR_CALL_NOT_IMPLEMENTED;
|
struct sc_manager_handle *manager = NULL;
|
||||||
|
struct sc_service_handle *service = NULL;
|
||||||
|
struct sc_notify_handle *notify;
|
||||||
|
struct sc_handle *hdr = handle;
|
||||||
|
|
||||||
|
WINE_TRACE("(%p, NotifyMask: 0x%x, %p, %p, %p, %p)\n", handle,
|
||||||
|
params.u.params->dwNotifyMask, clientprocessguid, scmprocessguid,
|
||||||
|
createremotequeue, hNotify);
|
||||||
|
|
||||||
|
switch (hdr->type)
|
||||||
|
{
|
||||||
|
case SC_HTYPE_SERVICE:
|
||||||
|
err = validate_service_handle(handle, SERVICE_QUERY_STATUS, &service);
|
||||||
|
break;
|
||||||
|
case SC_HTYPE_MANAGER:
|
||||||
|
err = validate_scm_handle(handle, SC_MANAGER_ENUMERATE_SERVICE, &manager);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err = ERROR_INVALID_HANDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != ERROR_SUCCESS)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (manager)
|
||||||
|
{
|
||||||
|
WARN("Need support for service creation/deletion notifications\n");
|
||||||
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
notify = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*notify));
|
||||||
|
if (!notify)
|
||||||
|
return ERROR_NOT_ENOUGH_SERVER_MEMORY;
|
||||||
|
|
||||||
|
notify->hdr.type = SC_HTYPE_NOTIFY;
|
||||||
|
notify->hdr.access = 0;
|
||||||
|
|
||||||
|
notify->service = service;
|
||||||
|
|
||||||
|
notify->event = CreateEventW(NULL, TRUE, FALSE, NULL);
|
||||||
|
|
||||||
|
notify->notify_mask = params.u.params->dwNotifyMask;
|
||||||
|
|
||||||
|
service_lock(service->service_entry);
|
||||||
|
|
||||||
|
if (service->service_entry->notify)
|
||||||
|
{
|
||||||
|
service_unlock(service->service_entry);
|
||||||
|
HeapFree(GetProcessHeap(), 0, notify);
|
||||||
|
return ERROR_ALREADY_REGISTERED;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask = 1 << (service->service_entry->status.dwCurrentState - SERVICE_STOPPED);
|
||||||
|
if (!service->service_entry->status_notified &&
|
||||||
|
(notify->notify_mask & mask))
|
||||||
|
{
|
||||||
|
fill_notify(notify);
|
||||||
|
service->service_entry->status_notified = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sc_notify_retain(notify);
|
||||||
|
service->service_entry->notify = notify;
|
||||||
|
}
|
||||||
|
|
||||||
|
sc_notify_retain(notify);
|
||||||
|
*hNotify = ¬ify->hdr;
|
||||||
|
|
||||||
|
service_unlock(service->service_entry);
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD __cdecl svcctl_GetNotifyResults(
|
DWORD __cdecl svcctl_GetNotifyResults(
|
||||||
SC_NOTIFY_RPC_HANDLE notify,
|
SC_NOTIFY_RPC_HANDLE hNotify,
|
||||||
SC_RPC_NOTIFY_PARAMS_LIST **params)
|
SC_RPC_NOTIFY_PARAMS_LIST **pList)
|
||||||
{
|
{
|
||||||
WINE_FIXME("\n");
|
DWORD err;
|
||||||
return ERROR_CALL_NOT_IMPLEMENTED;
|
struct sc_notify_handle *notify;
|
||||||
|
|
||||||
|
WINE_TRACE("(%p, %p)\n", hNotify, pList);
|
||||||
|
|
||||||
|
if (!pList)
|
||||||
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
*pList = NULL;
|
||||||
|
|
||||||
|
if ((err = validate_notify_handle(hNotify, 0, ¬ify)) != 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
sc_notify_retain(notify);
|
||||||
|
/* block until there is a result */
|
||||||
|
err = WaitForSingleObject(notify->event, INFINITE);
|
||||||
|
|
||||||
|
if (err != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
sc_notify_release(notify);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pList = InterlockedExchangePointer((void**)¬ify->params_list, NULL);
|
||||||
|
if (!*pList)
|
||||||
|
{
|
||||||
|
sc_notify_release(notify);
|
||||||
|
return ERROR_REQUEST_ABORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
sc_notify_release(notify);
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD __cdecl svcctl_CloseNotifyHandle(
|
DWORD __cdecl svcctl_CloseNotifyHandle(
|
||||||
SC_NOTIFY_RPC_HANDLE *notify,
|
SC_NOTIFY_RPC_HANDLE *hNotify,
|
||||||
BOOL *apc_fired)
|
BOOL *apc_fired)
|
||||||
{
|
{
|
||||||
WINE_FIXME("\n");
|
struct sc_notify_handle *notify;
|
||||||
return ERROR_CALL_NOT_IMPLEMENTED;
|
DWORD err;
|
||||||
|
|
||||||
|
WINE_TRACE("(%p, %p)\n", hNotify, apc_fired);
|
||||||
|
|
||||||
|
if ((err = validate_notify_handle(*hNotify, 0, ¬ify)) != 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
sc_notify_release(notify);
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD __cdecl svcctl_ControlServiceExA(
|
DWORD __cdecl svcctl_ControlServiceExA(
|
||||||
|
|
|
@ -45,6 +45,8 @@ struct process_entry
|
||||||
HANDLE overlapped_event;
|
HANDLE overlapped_event;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sc_notify_handle;
|
||||||
|
|
||||||
struct service_entry
|
struct service_entry
|
||||||
{
|
{
|
||||||
struct list entry;
|
struct list entry;
|
||||||
|
@ -63,6 +65,8 @@ struct service_entry
|
||||||
BOOL force_shutdown;
|
BOOL force_shutdown;
|
||||||
BOOL marked_for_delete;
|
BOOL marked_for_delete;
|
||||||
BOOL is_wow64;
|
BOOL is_wow64;
|
||||||
|
BOOL status_notified;
|
||||||
|
struct sc_notify_handle *notify;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct scmdatabase *active_database;
|
extern struct scmdatabase *active_database;
|
||||||
|
|
Loading…
Reference in New Issue