[PATCH 1/2] services: Add support for service status change notifications
Andrew Eikum
aeikum at codeweavers.com
Tue Jan 23 08:37:10 CST 2018
Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>
---
programs/services/rpc.c | 224 ++++++++++++++++++++++++++++++++++++++++---
programs/services/services.h | 4 +
2 files changed, 215 insertions(+), 13 deletions(-)
diff --git a/programs/services/rpc.c b/programs/services/rpc.c
index 20c5a2761f..7c3fa1e5e9 100644
--- a/programs/services/rpc.c
+++ b/programs/services/rpc.c
@@ -59,7 +59,8 @@ typedef enum
{
SC_HTYPE_DONT_CARE = 0,
SC_HTYPE_MANAGER,
- SC_HTYPE_SERVICE
+ SC_HTYPE_SERVICE,
+ SC_HTYPE_NOTIFY
} SC_HANDLE_TYPE;
struct sc_handle
@@ -80,6 +81,32 @@ struct sc_service_handle /* service handle */
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 scmdatabase *db;
@@ -227,6 +254,15 @@ static DWORD validate_service_handle(SC_RPC_HANDLE handle, DWORD needed_access,
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(
MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */
LPCWSTR DatabaseName,
@@ -274,6 +310,15 @@ static void SC_RPC_HANDLE_destroy(SC_RPC_HANDLE handle)
case SC_HTYPE_SERVICE:
{
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);
HeapFree(GetProcessHeap(), 0, service);
break;
@@ -776,13 +821,42 @@ DWORD __cdecl svcctl_ChangeServiceConfigW(
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(
SC_RPC_HANDLE hServiceStatus,
LPSERVICE_STATUS lpServiceStatus)
{
struct sc_service_handle *service;
struct process_entry *process;
- DWORD err;
+ DWORD err, mask;
WINE_TRACE("(%p, %p)\n", hServiceStatus, lpServiceStatus);
@@ -807,6 +881,19 @@ DWORD __cdecl svcctl_SetServiceStatus(
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);
return ERROR_SUCCESS;
@@ -1598,31 +1685,142 @@ DWORD __cdecl svcctl_unknown46(void)
}
DWORD __cdecl svcctl_NotifyServiceStatusChange(
- SC_RPC_HANDLE service,
+ SC_RPC_HANDLE handle,
SC_RPC_NOTIFY_PARAMS params,
GUID *clientprocessguid,
GUID *scmprocessguid,
BOOL *createremotequeue,
- SC_NOTIFY_RPC_HANDLE *notify)
+ SC_NOTIFY_RPC_HANDLE *hNotify)
{
- WINE_FIXME("\n");
- return ERROR_CALL_NOT_IMPLEMENTED;
+ DWORD err, mask;
+ 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(
- SC_NOTIFY_RPC_HANDLE notify,
- SC_RPC_NOTIFY_PARAMS_LIST **params)
+ SC_NOTIFY_RPC_HANDLE hNotify,
+ SC_RPC_NOTIFY_PARAMS_LIST **pList)
{
- WINE_FIXME("\n");
- return ERROR_CALL_NOT_IMPLEMENTED;
+ DWORD err;
+ 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(
- SC_NOTIFY_RPC_HANDLE *notify,
+ SC_NOTIFY_RPC_HANDLE *hNotify,
BOOL *apc_fired)
{
- WINE_FIXME("\n");
- return ERROR_CALL_NOT_IMPLEMENTED;
+ struct sc_notify_handle *notify;
+ 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(
diff --git a/programs/services/services.h b/programs/services/services.h
index cda70e0b60..fd19ed32f4 100644
--- a/programs/services/services.h
+++ b/programs/services/services.h
@@ -45,6 +45,8 @@ struct process_entry
HANDLE overlapped_event;
};
+struct sc_notify_handle;
+
struct service_entry
{
struct list entry;
@@ -63,6 +65,8 @@ struct service_entry
BOOL force_shutdown;
BOOL marked_for_delete;
BOOL is_wow64;
+ BOOL status_notified;
+ struct sc_notify_handle *notify;
};
extern struct scmdatabase *active_database;
--
2.16.0
More information about the wine-devel
mailing list