#include <TargetConditionals.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/types.h>
#include <servers/bootstrap.h>
#include "configd.h"
#include "configd_server.h"
#include "notify_server.h"
#include "session.h"
extern struct mig_subsystem _config_subsystem;
extern boolean_t config_server(mach_msg_header_t *, mach_msg_header_t *);
static CFMachPortRef configd_port = NULL;
__private_extern__
boolean_t
config_demux(mach_msg_header_t *request, mach_msg_header_t *reply)
{
Boolean processed = FALSE;
processed = config_server(request, reply);
if (processed) {
return TRUE;
}
processed = notify_server(request, reply);
if (processed) {
return TRUE;
}
SC_log(LOG_ERR, "unknown message ID (%d) received", request->msgh_id);
reply->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request->msgh_bits), 0);
reply->msgh_remote_port = request->msgh_remote_port;
reply->msgh_size = sizeof(mig_reply_error_t);
reply->msgh_local_port = MACH_PORT_NULL;
reply->msgh_id = request->msgh_id + 100;
((mig_reply_error_t *)reply)->NDR = NDR_record;
((mig_reply_error_t *)reply)->RetCode = MIG_BAD_ID;
return FALSE;
}
#define MACH_MSG_BUFFER_SIZE 128
__private_extern__
void
configdCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
mig_reply_error_t * bufRequest = msg;
uint32_t bufReply_q[MACH_MSG_BUFFER_SIZE/sizeof(uint32_t)];
mig_reply_error_t * bufReply = (mig_reply_error_t *)bufReply_q;
static CFIndex bufSize = 0;
mach_msg_return_t mr;
int options;
if (bufSize == 0) {
bufSize = _config_subsystem.maxsize;
if (bufSize > sizeof(bufReply_q)) {
SC_log(LOG_NOTICE, "buffer size should be increased > %d",
_config_subsystem.maxsize);
}
}
if (bufSize > sizeof(bufReply_q)) {
bufReply = CFAllocatorAllocate(NULL, _config_subsystem.maxsize, 0);
}
bufReply->RetCode = 0;
(void) config_demux(&bufRequest->Head, &bufReply->Head);
if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
if (bufReply->RetCode == MIG_NO_REPLY) {
bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
} else if ((bufReply->RetCode != KERN_SUCCESS) &&
(bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
mach_msg_destroy(&bufRequest->Head);
}
}
if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
options = MACH_SEND_MSG;
if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND_ONCE) {
options |= MACH_SEND_TIMEOUT;
}
mr = mach_msg(&bufReply->Head,
options,
bufReply->Head.msgh_size,
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
switch (mr) {
case MACH_SEND_INVALID_DEST:
case MACH_SEND_TIMED_OUT:
break;
default :
goto done;
}
}
if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
mach_msg_destroy(&bufReply->Head);
}
done :
if (bufReply != (mig_reply_error_t *)bufReply_q)
CFAllocatorDeallocate(NULL, bufReply);
return;
}
static CFStringRef
serverMPCopyDescription(const void *info)
{
return CFStringCreateWithFormat(NULL, NULL, CFSTR("<main DynamicStore MP>"));
}
__private_extern__
void
server_init()
{
serverSessionRef mySession;
int ret;
CFRunLoopSourceRef rls;
char *service_name;
mach_port_t service_port = MACH_PORT_NULL;
kern_return_t status;
service_name = getenv("SCD_SERVER");
if (service_name == NULL) {
service_name = SCD_SERVER;
}
status = bootstrap_check_in(bootstrap_port, service_name, &service_port);
switch (status) {
case BOOTSTRAP_SUCCESS :
break;
case BOOTSTRAP_NOT_PRIVILEGED :
SC_log(LOG_ERR, "'%s' server already starting", service_name);
exit (EX_UNAVAILABLE);
case BOOTSTRAP_SERVICE_ACTIVE :
SC_log(LOG_ERR, "'%s' server already active", service_name);
exit (EX_UNAVAILABLE);
default :
SC_log(LOG_ERR, "server_init bootstrap_check_in(..., '%s', ...) failed: %s",
service_name,
bootstrap_strerror(status));
exit (EX_UNAVAILABLE);
}
mySession = addSession(service_port, serverMPCopyDescription);
configd_port = mySession->serverPort;
rls = CFMachPortCreateRunLoopSource(NULL, configd_port, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
ret = pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0);
if (ret != 0) {
SC_log(LOG_ERR, "pthread_set_qos_class_self_np() failed: %s", strerror(errno));
}
return;
}
__private_extern__
int
server_shutdown()
{
if (configd_port != NULL) {
mach_port_t service_port = CFMachPortGetPort(configd_port);
CFMachPortInvalidate(configd_port);
CFRelease(configd_port);
configd_port = NULL;
if (service_port != MACH_PORT_NULL) {
(void) mach_port_mod_refs(mach_task_self(),
service_port,
MACH_PORT_RIGHT_RECEIVE,
-1);
}
}
return EX_OK;
}
__private_extern__
void
server_loop()
{
pthread_setname_np("SCDynamicStore");
while (TRUE) {
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, TRUE);
pushNotifications();
}
}