#include "SCDynamicStoreInternal.h"
#include "config.h"
#include <mach/mach_error.h>
static mach_msg_id_t
waitForMachMessage(mach_port_t port)
{
union {
u_int8_t buf[sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE];
mach_msg_empty_rcv_t msg;
} notify_msg;
kern_return_t status;
status = mach_msg(¬ify_msg.msg.header,
MACH_RCV_MSG,
0,
sizeof(notify_msg),
port,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (status != KERN_SUCCESS) {
SC_log(LOG_NOTICE, "mach_msg() failed: %s", mach_error_string(status));
return -1;
}
return notify_msg.msg.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) {
SC_log(LOG_ERR, "mach_port_allocate() failed: %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) {
SC_log(LOG_NOTICE, "mach_port_insert_right() failed: %s", mach_error_string(status));
_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) {
SC_log(LOG_NOTICE, "mach_port_request_notification() failed: %s", mach_error_string(status));
_SCErrorSet(status);
return FALSE;
}
if (oldNotify != MACH_PORT_NULL) {
SC_log(LOG_NOTICE, "oldNotify != MACH_PORT_NULL");
}
os_activity_scope(storePrivate->activity);
retry :
status = notifyviaport(storePrivate->server,
port,
0,
(int *)&sc_status);
if (__SCDynamicStoreCheckRetryAndHandleError(store,
status,
&sc_status,
"SCDynamicStoreNotifyWait notifyviaport()")) {
goto retry;
}
if (status != KERN_SUCCESS) {
if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) {
(void) mach_port_deallocate(mach_task_self(), port);
}
(void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
}
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
SC_log(LOG_DEBUG, "notifier port closed, port %d", port);
#endif
_SCErrorSet(kSCStatusNoStoreServer);
return FALSE;
}
if (msgid == -1) {
#ifdef DEBUG
SC_log(LOG_DEBUG, "communication with server failed, remove port right %d", port);
#endif
(void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE , -1);
_SCErrorSet(kSCStatusNoStoreServer);
return FALSE;
}
os_activity_scope(storePrivate->activity);
status = notifycancel(storePrivate->server,
(int *)&sc_status);
if (__SCDynamicStoreCheckRetryAndHandleError(store,
status,
&sc_status,
"SCDynamicStoreNotifyWait notifycancel()")) {
sc_status = kSCStatusOK;
}
(void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE , -1);
if (sc_status != kSCStatusOK) {
_SCErrorSet(sc_status);
return FALSE;
}
return TRUE;
}