#include <mach/mach.h>
#include <mach/mach_error.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include "SCDynamicStoreInternal.h"
#include "config.h"
static mach_msg_id_t
waitForMachMessage(mach_port_t port)
{
kern_return_t status;
mach_msg_empty_rcv_t *buf;
mach_msg_size_t size = sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE;
status = vm_allocate(mach_task_self(), (vm_address_t *)&buf, size, TRUE);
if (status != KERN_SUCCESS) {
SCLog(TRUE, LOG_DEBUG, CFSTR("waitForMachMessage vm_allocate(): %s"), mach_error_string(status));
return -1;
}
status = mach_msg(&buf->header,
MACH_RCV_MSG,
0,
size,
port,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (status != KERN_SUCCESS) {
SCLog(TRUE, LOG_DEBUG, CFSTR("waitForMachMessage mach_msg(): %s"), mach_error_string(status));
return -1;
}
return buf->header.msgh_id;
}
Boolean
SCDynamicStoreNotifyWait(SCDynamicStoreRef store)
{
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
kern_return_t status;
mach_port_t port;
mach_port_t oldNotify;
int sc_status;
mach_msg_id_t msgid;
if (store == NULL) {
_SCErrorSet(kSCStatusNoStoreSession);
return FALSE;
}
if (storePrivate->server == MACH_PORT_NULL) {
_SCErrorSet(kSCStatusNoStoreServer);
return FALSE;
}
if (storePrivate->notifyStatus != NotifierNotRegistered) {
_SCErrorSet(kSCStatusNotifierActive);
return FALSE;
}
status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
if (status != KERN_SUCCESS) {
SCLog(TRUE, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyWait mach_port_allocate(): %s"), mach_error_string(status));
_SCErrorSet(status);
return FALSE;
}
status = mach_port_insert_right(mach_task_self(),
port,
port,
MACH_MSG_TYPE_MAKE_SEND);
if (status != KERN_SUCCESS) {
SCLog(TRUE, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyWait mach_port_insert_right(): %s"), mach_error_string(status));
(void) mach_port_destroy(mach_task_self(), port);
_SCErrorSet(status);
return FALSE;
}
status = mach_port_request_notification(mach_task_self(),
port,
MACH_NOTIFY_NO_SENDERS,
1,
port,
MACH_MSG_TYPE_MAKE_SEND_ONCE,
&oldNotify);
if (status != KERN_SUCCESS) {
SCLog(TRUE, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyWait mach_port_request_notification(): %s"), mach_error_string(status));
(void) mach_port_destroy(mach_task_self(), port);
_SCErrorSet(status);
return FALSE;
}
#ifdef DEBUG
if (oldNotify != MACH_PORT_NULL) {
SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyWait(): why is oldNotify != MACH_PORT_NULL?"));
}
#endif
status = notifyviaport(storePrivate->server,
port,
0,
(int *)&sc_status);
if (status != KERN_SUCCESS) {
#ifdef DEBUG
if (status != MACH_SEND_INVALID_DEST)
SCLog(TRUE, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyWait notifyviaport(): %s"), mach_error_string(status));
#endif
(void) mach_port_destroy(mach_task_self(), storePrivate->server);
storePrivate->server = MACH_PORT_NULL;
_SCErrorSet(status);
return FALSE;
}
if (sc_status != kSCStatusOK) {
_SCErrorSet(sc_status);
return FALSE;
}
storePrivate->notifyStatus = Using_NotifierWait;
msgid = waitForMachMessage(port);
storePrivate->notifyStatus = NotifierNotRegistered;
if (msgid == MACH_NOTIFY_NO_SENDERS) {
#ifdef DEBUG
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyWait notifier port closed, destroying port %d"), port);
#endif
_SCErrorSet(kSCStatusNoStoreServer);
return FALSE;
}
if (msgid == -1) {
#ifdef DEBUG
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyWait communication with server failed, destroying port %d"), port);
#endif
(void) mach_port_destroy(mach_task_self(), port);
_SCErrorSet(kSCStatusNoStoreServer);
return FALSE;
}
status = notifycancel(storePrivate->server,
(int *)&sc_status);
if (status != KERN_SUCCESS) {
#ifdef DEBUG
if (status != MACH_SEND_INVALID_DEST)
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyWait notifycancel(): %s"), mach_error_string(status));
#endif
(void) mach_port_destroy(mach_task_self(), storePrivate->server);
storePrivate->server = MACH_PORT_NULL;
_SCErrorSet(status);
return FALSE;
}
(void) mach_port_destroy(mach_task_self(), port);
return TRUE;
}