#include <mach/mach.h>
#include <mach/mach_error.h>
#include <SystemConfiguration/SCD.h>
#include "config.h"
#include "SCDPrivate.h"
SCDStatus
SCDNotifierWait(SCDSessionRef session)
{
SCDSessionPrivateRef sessionPrivate = (SCDSessionPrivateRef)session;
kern_return_t status;
mach_port_t port;
mach_port_t oldNotify;
SCDStatus scd_status;
mach_msg_id_t msgid;
SCDLog(LOG_DEBUG, CFSTR("SCDNotifierWait:"));
if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
return SCD_NOSESSION;
}
if (SCDOptionGet(NULL, kSCDOptionIsServer)) {
return SCD_FAILED;
}
if (sessionPrivate->notifyStatus != NotifierNotRegistered) {
return SCD_NOTIFIERACTIVE;
}
SCDLog(LOG_DEBUG, CFSTR("Allocating port (for server response)"));
status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
if (status != KERN_SUCCESS) {
SCDLog(LOG_DEBUG, CFSTR("mach_port_allocate(): %s"), mach_error_string(status));
return SCD_FAILED;
}
SCDLog(LOG_DEBUG, CFSTR(" port = %d"), port);
status = mach_port_insert_right(mach_task_self(),
port,
port,
MACH_MSG_TYPE_MAKE_SEND);
if (status != KERN_SUCCESS) {
SCDLog(LOG_DEBUG, CFSTR("mach_port_insert_right(): %s"), mach_error_string(status));
(void) mach_port_destroy(mach_task_self(), port);
return SCD_FAILED;
}
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) {
SCDLog(LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
(void) mach_port_destroy(mach_task_self(), port);
return SCD_FAILED;
}
#ifdef DEBUG
if (oldNotify != MACH_PORT_NULL) {
SCDLog(LOG_DEBUG, CFSTR("SCDNotifierWait(): why is oldNotify != MACH_PORT_NULL?"));
}
#endif
SCDLog(LOG_DEBUG, CFSTR("Requesting notification via mach port %d"), port);
status = notifyviaport(sessionPrivate->server,
port,
0,
(int *)&scd_status);
if (status != KERN_SUCCESS) {
if (status != MACH_SEND_INVALID_DEST)
SCDLog(LOG_DEBUG, CFSTR("notifyviaport(): %s"), mach_error_string(status));
(void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
sessionPrivate->server = MACH_PORT_NULL;
return SCD_NOSERVER;
}
if (scd_status != SCD_OK) {
return scd_status;
}
sessionPrivate->notifyStatus = Using_NotifierWait;
SCDLog(LOG_DEBUG, CFSTR("Waiting..."));
msgid = _waitForMachMessage(port);
sessionPrivate->notifyStatus = NotifierNotRegistered;
if (msgid == MACH_NOTIFY_NO_SENDERS) {
SCDLog(LOG_DEBUG, CFSTR(" notifier port closed, destroying port %d"), port);
return SCD_NOSERVER;
}
if (msgid == -1) {
SCDLog(LOG_DEBUG, CFSTR(" communication with server failed, destroying port %d"), port);
(void) mach_port_destroy(mach_task_self(), port);
return SCD_NOSERVER;
}
SCDLog(LOG_DEBUG, CFSTR("Something changed, cancelling notification request"));
status = notifycancel(sessionPrivate->server,
(int *)&scd_status);
if (status != KERN_SUCCESS) {
if (status != MACH_SEND_INVALID_DEST)
SCDLog(LOG_DEBUG, CFSTR("notifycancel(): %s"), mach_error_string(status));
(void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
sessionPrivate->server = MACH_PORT_NULL;
scd_status = SCD_NOSERVER;
}
(void) mach_port_destroy(mach_task_self(), port);
return scd_status;
}