#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "configd.h"
#include "session.h"
__private_extern__
int
__SCDynamicStoreNotifyFileDescriptor(SCDynamicStoreRef store,
int32_t identifier,
int *fd)
{
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
int sock;
CFStringRef sessionKey;
CFDictionaryRef info;
SCLog(_configd_verbose, LOG_DEBUG, CFSTR("__SCDynamicStoreNotifyFileDescriptor:"));
if (!store || (storePrivate->server == MACH_PORT_NULL)) {
return kSCStatusNoStoreSession;
}
if (storePrivate->notifyStatus != NotifierNotRegistered) {
return kSCStatusNotifierActive;
}
if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
SCLog(_configd_verbose, LOG_NOTICE, CFSTR("socket: %s"), strerror(errno));
return kSCStatusFailed;
}
*fd = sock;
sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), storePrivate->server);
info = CFDictionaryGetValue(sessionData, sessionKey);
CFRelease(sessionKey);
if (info && CFDictionaryContainsKey(info, kSCDChangedKeys)) {
CFNumberRef sessionNum;
if (needsNotification == NULL)
needsNotification = CFSetCreateMutable(NULL,
0,
&kCFTypeSetCallBacks);
sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &storePrivate->server);
CFSetAddValue(needsNotification, sessionNum);
CFRelease(sessionNum);
}
return kSCStatusOK;
}
__private_extern__
kern_return_t
_notifyviafd(mach_port_t server,
xmlData_t pathRef,
mach_msg_type_number_t pathLen,
int identifier,
int *sc_status
)
{
kern_return_t status;
serverSessionRef mySession = getSession(server);
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)mySession->store;
struct sockaddr_un un;
int sock;
int bufSiz = sizeof(storePrivate->notifyFileIdentifier);
int nbioYes = 1;
if (_configd_verbose) {
SCLog(TRUE, LOG_DEBUG, CFSTR("Send message via UNIX domain socket when a notification key changes."));
SCLog(TRUE, LOG_DEBUG, CFSTR(" server = %d"), server);
SCLog(TRUE, LOG_DEBUG, CFSTR(" path = %s"), pathRef);
}
if (pathLen > (sizeof(un.sun_path) - 1)) {
SCLog(_configd_verbose, LOG_NOTICE, CFSTR("domain socket path length too long!"));
status = vm_deallocate(mach_task_self(), (vm_address_t)pathRef, pathLen);
if (status != KERN_SUCCESS) {
SCLog(_configd_verbose, LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
}
*sc_status = kSCStatusFailed;
return KERN_SUCCESS;
}
un.sun_family = AF_UNIX;
bcopy(pathRef, un.sun_path, pathLen);
un.sun_path[pathLen] = '\0';
status = vm_deallocate(mach_task_self(), (vm_address_t)pathRef, pathLen);
if (status != KERN_SUCCESS) {
SCLog(_configd_verbose, LOG_DEBUG, CFSTR("vm_deallocate(): %s"), mach_error_string(status));
}
if (!mySession) {
*sc_status = kSCStatusNoStoreSession;
return KERN_SUCCESS;
}
*sc_status = __SCDynamicStoreNotifyFileDescriptor(mySession->store, identifier, &sock);
if (*sc_status != kSCStatusOK) {
return KERN_SUCCESS;
}
if (connect(sock, (struct sockaddr *)&un, sizeof(un)) == -1) {
SCLog(_configd_verbose, LOG_DEBUG, CFSTR("connect: %s"), strerror(errno));
(void) close(sock);
storePrivate->notifyStatus = NotifierNotRegistered;
storePrivate->notifyFile = -1;
*sc_status = kSCStatusFailed;
return KERN_SUCCESS;
}
SCLog(_configd_verbose, LOG_NOTICE, CFSTR(" fd = %d"), sock);
(void) unlink(un.sun_path);
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &bufSiz, sizeof(bufSiz)) < 0) {
SCLog(_configd_verbose, LOG_DEBUG, CFSTR("setsockopt: %s"), strerror(errno));
(void) close(sock);
*sc_status = kSCStatusFailed;
return KERN_SUCCESS;
}
if (ioctl(sock, FIONBIO, &nbioYes) == -1) {
SCLog(_configd_verbose, LOG_DEBUG, CFSTR("ioctl(,FIONBIO,): %s"), strerror(errno));
(void) close(sock);
*sc_status = kSCStatusFailed;
return KERN_SUCCESS;
}
storePrivate->notifyStatus = Using_NotifierInformViaFD;
storePrivate->notifyFile = sock;
storePrivate->notifyFileIdentifier = identifier;
return KERN_SUCCESS;
}