#include <Kerberos/kipc_server.h>
#include <Kerberos/kipc_session.h>
#include "notifyServer.h"
static mach_port_t g_service_port = MACH_PORT_NULL;
static mach_port_t g_listen_port_set = MACH_PORT_NULL;
static kipc_boolean_t g_ready_to_quit = FALSE;
static kipc_demux_proc g_demux_proc = NULL;
#pragma mark -
mach_port_t
kipc_server_get_service_port ()
{
return g_service_port;
}
mach_port_t
kipc_server_get_listen_portset ()
{
return g_listen_port_set;
}
#pragma mark -
kipc_boolean_t
kipc_server_quit (void)
{
dprintf ("mach_server_quit_self(): quitting...");
g_ready_to_quit = true;
return g_ready_to_quit;
}
#pragma mark -
static kipc_boolean_t
kipc_server_demux (mach_msg_header_t *request, mach_msg_header_t *reply)
{
kipc_boolean_t handled = false;
if (!handled) {
handled = g_demux_proc (request, reply);
}
if (!handled) {
handled = mach_notify_server (request, reply);
}
return handled;
}
#pragma mark -
static kipc_err_t
kipc_get_server_id (char **out_server_id)
{
kern_return_t err = KERN_SUCCESS;
CFBundleRef bundle = NULL;
CFStringRef id_string = NULL;
CFIndex id_length = 0;
char *server_id = NULL;
if (out_server_id == NULL) { err = kipc_err (EINVAL); }
if (!err) {
bundle = CFBundleGetMainBundle ();
if (bundle == NULL) { err = ENOENT; }
}
if (!err) {
id_string = CFBundleGetIdentifier (bundle);
if (id_string == NULL) { err = ENOMEM; }
}
if (!err) {
id_length = CFStringGetMaximumSizeForEncoding (CFStringGetLength (id_string),
CFStringGetSystemEncoding ()) + 1;
server_id = calloc (id_length, sizeof (char));
if (server_id == NULL) { err = errno; }
}
if (!err) {
if (!CFStringGetCString (id_string, server_id, id_length, CFStringGetSystemEncoding ())) {
err = ENOMEM;
}
}
if (!err) {
*out_server_id = server_id;
server_id = NULL;
}
if (server_id != NULL) { kipc_free_string (server_id); }
return kipc_err (err);
}
kipc_err_t
kipc_server_run_server (kipc_demux_proc in_demux_proc)
{
kern_return_t err = KERN_SUCCESS;
char *server_id = NULL;
char *service_name = NULL;
char *lookup_name = NULL;
mach_port_t boot_port = MACH_PORT_NULL;
mach_port_t lookup_port = MACH_PORT_NULL;
mach_port_t notify_port = MACH_PORT_NULL;
mach_port_t previous_notify_port = MACH_PORT_NULL;
if (in_demux_proc == NULL) { err = kipc_err (EINVAL); }
if (!err && (geteuid () == 0)) {
uid_t new_uid = kipc_session_get_server_uid ();
if (setuid (new_uid) < 0) {
dprintf ("%s(): setuid(%d) failed (euid is %d)", __FUNCTION__, new_uid, geteuid ());
}
}
if (!err) {
g_demux_proc = in_demux_proc;
}
if (!err) {
err = kipc_get_server_id (&server_id);
}
if (!err) {
err = kipc_get_service_name (&service_name, server_id);
}
if (!err) {
err = kipc_get_lookup_name (&lookup_name, server_id);
}
if (!err) {
err = task_get_bootstrap_port (mach_task_self (), &boot_port);
dprintf ("%s(): task_get_bootstrap_port(): port is %x (err = %d '%s')",
__FUNCTION__, boot_port, err, mach_error_string (err));
}
if (!err) {
err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &lookup_port);
}
if (!err) {
err = mach_port_insert_right (mach_task_self (), lookup_port, lookup_port, MACH_MSG_TYPE_MAKE_SEND);
}
if (!err) {
err = bootstrap_register (boot_port, lookup_name, lookup_port);
dprintf ("%s(): bootstrap_register('%s', %x): (err = %d '%s')",
__FUNCTION__, lookup_name, lookup_port, err, mach_error_string (err));
}
if (!err) {
err = bootstrap_check_in (boot_port, (char *) service_name, &g_service_port);
dprintf ("%s(): bootstrap_check_in('%s'): port is %d (err = %d '%s')",
__FUNCTION__, service_name, g_service_port, err, mach_error_string (err));
}
if (!err) {
err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, ¬ify_port);
}
if (!err) {
err = mach_port_request_notification (mach_task_self (), g_service_port, MACH_NOTIFY_NO_SENDERS, true,
notify_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous_notify_port);
dprintf ("%s(): requesting notification for no senders of %x returned '%s', err = %d\n",
__FUNCTION__, g_service_port, mach_error_string (err), err);
}
if (!err) {
err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_PORT_SET, &g_listen_port_set);
}
if (!err) {
err = mach_port_move_member (mach_task_self (), g_service_port, g_listen_port_set);
}
if (!err) {
err = mach_port_move_member (mach_task_self (), notify_port, g_listen_port_set);
}
if (!err) {
dprintf ("%s(): \"%s\": starting up. service port = %x, bootstrap port = %x\n",
__FUNCTION__, service_name, g_service_port, boot_port);
}
while (!err && !g_ready_to_quit) {
err = mach_msg_server_once (kipc_server_demux, kkipc_max_message_size, g_listen_port_set, MACH_MSG_OPTION_NONE);
}
if (g_service_port != MACH_PORT_NULL) {
err = mach_port_request_notification (mach_task_self (), g_service_port, MACH_NOTIFY_NO_SENDERS,
true, MACH_PORT_NULL, MACH_MSG_TYPE_MAKE_SEND_ONCE,
&previous_notify_port);
dprintf ("%s(): removing notification for no senders of %x returned '%s', err = %d\n",
__FUNCTION__, previous_notify_port, mach_error_string (err), err);
}
if (lookup_port != MACH_PORT_NULL) {
kipc_err_t terr = bootstrap_register (boot_port, lookup_name, MACH_PORT_NULL);
dprintf ("%s(): bootstrap_register('%s', MACH_PORT_NULL): (err = %d '%s')",
__FUNCTION__, lookup_name, terr, mach_error_string (terr));
mach_port_deallocate (mach_task_self (), lookup_port);
}
if (notify_port != MACH_PORT_NULL) { mach_port_deallocate (mach_task_self (), notify_port); }
if (boot_port != MACH_PORT_NULL) { mach_port_deallocate (mach_task_self (), boot_port); }
if (lookup_name != NULL ) { kipc_free_string (lookup_name); }
if (service_name != NULL ) { kipc_free_string (service_name); }
if (server_id != NULL ) { kipc_free_string (server_id); }
return kipc_err (err);
}
#pragma mark -
kern_return_t
do_mach_notify_port_deleted (mach_port_t notify, mach_port_name_t name)
{
dprintf ("Received MACH_NOTIFY_PORT_DELETED... quitting self");
kipc_server_quit ();
return KERN_SUCCESS;
}
kern_return_t
do_mach_notify_port_destroyed (mach_port_t notify, mach_port_t rights)
{
dprintf ("Received MACH_NOTIFY_PORT_DESTROYED... quitting self");
kipc_server_quit ();
return KERN_SUCCESS;
}
kern_return_t
do_mach_notify_no_senders (mach_port_t notify, mach_port_mscount_t mscount)
{
dprintf ("Received MACH_NOTIFY_NO_SENDERS... quitting self");
kipc_server_quit ();
return KERN_SUCCESS;
}
kern_return_t
do_mach_notify_send_once (mach_port_t notify)
{
dprintf ("Received MACH_NOTIFY_SEND_ONCE");
return KERN_SUCCESS;
}
kern_return_t
do_mach_notify_dead_name (mach_port_t notify, mach_port_name_t name)
{
dprintf ("Received MACH_NOTIFY_DEAD_NAME... quitting self");
kipc_server_quit ();
return KERN_SUCCESS;
}