将 mach 端口传递给子进程
Pass mach port to child process
我正在尝试将 mach 端口传递给在 Mac OSX 上使用 fork
创建的子进程。我看到了这个 SO 问题 Sharing Mach ports with child processes but it doesn't have a solution it just describes the problem. Looking at this site https://robert.sesek.com/2014/1/changes_to_xnu_mach_ipc.html 它包含有关将 mach 端口传递给子进程的说明,但不幸的是它们不是示例代码。
我尝试实现端口交换,但子进程无法接收父进程发送的消息,recv_port
内部的 mach_msg
失败并返回 invalid name
。以下是我到目前为止所拥有的。抱歉,代码太多,mach IPC 有点让人难以简短。
既然 bootstrap 端口 hack 不再有效,我该如何将 mach 端口传递给 Mac OSX 上的子进程?
编辑
我更改了代码示例以反映 Ken Thomases 在他的回答中提出的要点,子进程创建一个具有发送权限的端口并将其发送给父进程。但是父进程无法接收子进程创建和发送的端口,只是挂在 recv_port
.
#include <stdio.h>
#include <mach/mach.h>
#include <mach/error.h>
#include <mach/message.h>
#include <unistd.h>
static int32_t
send_port(mach_port_t remote_port, mach_port_t port)
{
kern_return_t err;
struct
{
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t task_port;
} msg;
msg.header.msgh_remote_port = remote_port;
msg.header.msgh_local_port = MACH_PORT_NULL;
msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0) |
MACH_MSGH_BITS_COMPLEX;
msg.header.msgh_size = sizeof msg;
msg.body.msgh_descriptor_count = 1;
msg.task_port.name = port;
msg.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
msg.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
err = mach_msg_send(&msg.header);
if(err != KERN_SUCCESS)
{
mach_error("Can't send mach msg\n", err);
return (-1);
}
return (0);
}
static int32_t
recv_port(mach_port_t recv_port, mach_port_t *port)
{
kern_return_t err;
struct
{
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t task_port;
mach_msg_trailer_t trailer;
} msg;
err = mach_msg(&msg.header, MACH_RCV_MSG,
0, sizeof msg, recv_port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if(err != KERN_SUCCESS)
{
mach_error("Can't recieve mach message\n", err);
return (-1);
}
*port = msg.task_port.name;
return 0;
}
static int32_t
setup_recv_port(mach_port_t *recv_port)
{
kern_return_t err;
mach_port_t port = MACH_PORT_NULL;
err = mach_port_allocate(mach_task_self (),
MACH_PORT_RIGHT_RECEIVE, &port);
if(err != KERN_SUCCESS)
{
mach_error("Can't allocate mach port\n", err);
return (-1);
}
err = mach_port_insert_right(mach_task_self (),
port,
port,
MACH_MSG_TYPE_MAKE_SEND);
if(err != KERN_SUCCESS)
{
mach_error("Can't insert port right\n", err);
return (-1);
}
(*recv_port) = port;
return (0);
}
pid_t
fork_pass_port(mach_port_t pass_port, int32_t (*child_start)(mach_port_t port, void *arg), void *arg)
{
pid_t pid = 0;
int32_t rtrn = 0;
kern_return_t err;
mach_port_t special_port = MACH_PORT_NULL;
/* Setup the mach port. */
if(setup_recv_port(&pass_port) != 0)
{
printf("Can't setup mach port\n");
return (-1);
}
/* Grab our current task's(process's) HOST_NAME special port. */
err = task_get_special_port(mach_task_self(), TASK_HOST_PORT, &special_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't get special port:\n", err);
return (-1);
}
/* Set the HOST_NAME special port as the parent recv port. */
err = task_set_special_port(mach_task_self(), TASK_HOST_PORT, pass_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't set special port:\n", err);
return (-1);
}
pid = fork();
if(pid == 0)
{
mach_port_t host_port = MACH_PORT_NULL;
mach_port_t port = MACH_PORT_NULL;
/* In the child process grab the port passed by the parent. */
err = task_get_special_port(mach_task_self(), TASK_HOST_PORT, &pass_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't get special port:\n", err);
return (-1);
}
/* Create a port with a send right. */
if(setup_recv_port(&port) != 0)
{
printf("Can't setup mach port\n");
return (-1);
}
/* Send port to parent. */
rtrn = send_port(pass_port, port);
if(rtrn < 0)
{
printf("Can't send port\n");
return (-1);
}
/* Now that were done passing the mach port, start the function passed by the caller. */
child_start(pass_port, arg);
/* Exit and clean up the child process. */
_exit(0);
}
else if(pid > 0)
{
mach_port_t child_port = MACH_PORT_NULL;
rtrn = recv_port(pass_port, &child_port);
if(rtrn < 0)
{
printf("Can't recv port\n");
return (-1);
}
/* Reset parents special port. */
err = task_set_special_port(mach_task_self(), TASK_HOST_PORT, special_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't set special port:\n", err);
return (-1);
}
return (0);
}
else
{
/* Error, so cleanup the mach port. */
err = mach_port_deallocate(mach_task_self(), pass_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't deallocate mach port\n", err);
return (-1);
}
perror("fork");
return (-1);
}
}
static int32_t start(mach_port_t port, void *arg)
{
printf("Started\n");
return (0);
}
int main(void)
{
char *arg = "argument";
mach_port_t port = MACH_PORT_NULL;
pid_t pid = fork_pass_port(port, start, arg);
if(pid < 0)
{
printf("Can't fork and pass msg port\n");
return (-1);
}
return (0);
}
任何给定端口都只有一个接收权。 parent 对其创建的端口具有接收权。特殊端口的继承仅针对发送权限。所以,child只继承你通讯端口的发送权。
您链接到的文章暗示 child 通过此端口向 parent 发送消息。 child 应该已经创建了自己的具有接收权限的新端口。它的消息将携带该端口的发送或 send-once 权利到 parent,具体取决于您是否需要正在进行的双工通信。 child 会将 send(-once) 放在消息的 msgh_local_port
中; parent 将在 msgh_remote_port
中收到它。 parent 可以使用该发送(-一次)权限回复,回复可以携带发送到原始主机端口的权限。 child 可以使用它来恢复其主机端口。
或者,您可以这样做:
- 传递给child,通过特殊的端口继承,parent的任务端口的发送权;有了这个,child 几乎可以对 parent
做任何事情
- 让 child 使用
mach_port_extract_right()
从 parent 中提取您的通信端口的接收权
- 让它取消分配parent任务端口的发送权,只是为了安全
还可以提取原主机端口的发送权,而不是通过IPC接收,应该更简单。
综上所述,是什么让您认为以这种方式使用主机端口比 bootstrap 端口更安全?
我想出了如何通过特殊端口继承来传递 mach 端口。在调用 fork 之前,您必须暂时将 TASK_BOOTSTRAP_PORT
替换为您要通过的端口。其他特殊端口以某种方式失败。下面是 "port swap dance".
的示例
请注意,此代码仅在 OSX 10.11.3 上测试过,可能不适用于 OSX 的先前或未来版本。
#include <stdio.h>
#include <mach/mach.h>
#include <mach/error.h>
#include <mach/message.h>
#include <unistd.h>
#define SPECIAL_PORT TASK_BOOTSTRAP_PORT
static int32_t
send_port(mach_port_t remote_port, mach_port_t port)
{
kern_return_t err;
struct
{
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t task_port;
} msg;
msg.header.msgh_remote_port = remote_port;
msg.header.msgh_local_port = MACH_PORT_NULL;
msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0) |
MACH_MSGH_BITS_COMPLEX;
msg.header.msgh_size = sizeof msg;
msg.body.msgh_descriptor_count = 1;
msg.task_port.name = port;
msg.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
msg.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
err = mach_msg_send(&msg.header);
if(err != KERN_SUCCESS)
{
mach_error("Can't send mach msg\n", err);
return (-1);
}
return (0);
}
static int32_t
recv_port(mach_port_t recv_port, mach_port_t *port)
{
kern_return_t err;
struct
{
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t task_port;
mach_msg_trailer_t trailer;
} msg;
err = mach_msg(&msg.header, MACH_RCV_MSG,
0, sizeof msg, recv_port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if(err != KERN_SUCCESS)
{
mach_error("Can't recieve mach message\n", err);
return (-1);
}
(*port) = msg.task_port.name;
return 0;
}
static int32_t
setup_recv_port(mach_port_t *recv_port)
{
kern_return_t err;
mach_port_t port = MACH_PORT_NULL;
err = mach_port_allocate(mach_task_self (),
MACH_PORT_RIGHT_RECEIVE, &port);
if(err != KERN_SUCCESS)
{
mach_error("Can't allocate mach port\n", err);
return (-1);
}
err = mach_port_insert_right(mach_task_self (),
port,
port,
MACH_MSG_TYPE_MAKE_SEND);
if(err != KERN_SUCCESS)
{
mach_error("Can't insert port right\n", err);
return (-1);
}
(*recv_port) = port;
return (0);
}
static int32_t
start(mach_port_t port, void *arg)
{
return (0);
}
static pid_t
fork_pass_port(mach_port_t *pass_port,
int32_t (*child_start)(mach_port_t port, void *arg),
void *arg)
{
pid_t pid = 0;
int32_t rtrn = 0;
kern_return_t err;
mach_port_t special_port = MACH_PORT_NULL;
/* Allocate the mach port. */
if(setup_recv_port(pass_port) != 0)
{
printf("Can't setup mach port\n");
return (-1);
}
/* Grab our current process's bootstrap port. */
err = task_get_special_port(mach_task_self(), SPECIAL_PORT, &special_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't get special port:\n", err);
return (-1);
}
/* Set the special port as the parent recv port. */
err = task_set_special_port(mach_task_self(), SPECIAL_PORT, (*pass_port));
if(err != KERN_SUCCESS)
{
mach_error("Can't set special port:\n", err);
return (-1);
}
pid = fork();
if(pid == 0)
{
mach_port_t bootstrap_port = MACH_PORT_NULL;
mach_port_t port = MACH_PORT_NULL;
/* In the child process grab the port passed by the parent. */
err = task_get_special_port(mach_task_self(), SPECIAL_PORT, pass_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't get special port:\n", err);
return (-1);
}
/* Create a port with a send right. */
if(setup_recv_port(&port) != 0)
{
printf("Can't setup mach port\n");
return (-1);
}
/* Send port to parent. */
rtrn = send_port((*pass_port), port);
if(rtrn < 0)
{
printf("Can't send port\n");
return (-1);
}
/* Receive the real bootstrap port from the parent. */
rtrn = recv_port(port, &bootstrap_port);
if(rtrn < 0)
{
printf("Can't receive bootstrap port\n");
return (-1);
}
/* Set the bootstrap port back to normal. */
err = task_set_special_port(mach_task_self(), SPECIAL_PORT, bootstrap_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't set special port:\n", err);
return (-1);
}
/* Now that were done with the port dance, start the function passed by the caller. */
child_start((*pass_port), arg);
/* Exit and clean up the child process. */
_exit(0);
}
else if(pid > 0)
{
mach_port_t child_port = MACH_PORT_NULL;
/* Grab the child's recv port. */
rtrn = recv_port((*pass_port), &child_port);
if(rtrn < 0)
{
printf("Can't recv port\n");
return (-1);
}
/* Send the child the original bootstrap port. */
rtrn = send_port(child_port, special_port);
if(rtrn < 0)
{
printf("Can't send bootstrap port\n");
return (-1);
}
/* Reset parents special port. */
err = task_set_special_port(mach_task_self(), SPECIAL_PORT, special_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't set special port:\n", err);
return (-1);
}
return (0);
}
else
{
/* Error, so cleanup the mach port. */
err = mach_port_deallocate(mach_task_self(), (*pass_port));
if(err != KERN_SUCCESS)
{
mach_error("Can't deallocate mach port\n", err);
return (-1);
}
perror("fork");
return (-1);
}
}
int main(void)
{
/* Argument to pass to the child process. */
char *arg = "argument";
/* Mach port we want to pass to the child. */
mach_port_t port = MACH_PORT_NULL;
pid_t pid = fork_pass_port(&port, start, arg);
if(pid < 0)
{
printf("Can't fork and pass msg port\n");
return (-1);
}
return (0);
}
我正在尝试将 mach 端口传递给在 Mac OSX 上使用 fork
创建的子进程。我看到了这个 SO 问题 Sharing Mach ports with child processes but it doesn't have a solution it just describes the problem. Looking at this site https://robert.sesek.com/2014/1/changes_to_xnu_mach_ipc.html 它包含有关将 mach 端口传递给子进程的说明,但不幸的是它们不是示例代码。
我尝试实现端口交换,但子进程无法接收父进程发送的消息,recv_port
内部的 mach_msg
失败并返回 invalid name
。以下是我到目前为止所拥有的。抱歉,代码太多,mach IPC 有点让人难以简短。
既然 bootstrap 端口 hack 不再有效,我该如何将 mach 端口传递给 Mac OSX 上的子进程?
编辑
我更改了代码示例以反映 Ken Thomases 在他的回答中提出的要点,子进程创建一个具有发送权限的端口并将其发送给父进程。但是父进程无法接收子进程创建和发送的端口,只是挂在 recv_port
.
#include <stdio.h>
#include <mach/mach.h>
#include <mach/error.h>
#include <mach/message.h>
#include <unistd.h>
static int32_t
send_port(mach_port_t remote_port, mach_port_t port)
{
kern_return_t err;
struct
{
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t task_port;
} msg;
msg.header.msgh_remote_port = remote_port;
msg.header.msgh_local_port = MACH_PORT_NULL;
msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0) |
MACH_MSGH_BITS_COMPLEX;
msg.header.msgh_size = sizeof msg;
msg.body.msgh_descriptor_count = 1;
msg.task_port.name = port;
msg.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
msg.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
err = mach_msg_send(&msg.header);
if(err != KERN_SUCCESS)
{
mach_error("Can't send mach msg\n", err);
return (-1);
}
return (0);
}
static int32_t
recv_port(mach_port_t recv_port, mach_port_t *port)
{
kern_return_t err;
struct
{
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t task_port;
mach_msg_trailer_t trailer;
} msg;
err = mach_msg(&msg.header, MACH_RCV_MSG,
0, sizeof msg, recv_port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if(err != KERN_SUCCESS)
{
mach_error("Can't recieve mach message\n", err);
return (-1);
}
*port = msg.task_port.name;
return 0;
}
static int32_t
setup_recv_port(mach_port_t *recv_port)
{
kern_return_t err;
mach_port_t port = MACH_PORT_NULL;
err = mach_port_allocate(mach_task_self (),
MACH_PORT_RIGHT_RECEIVE, &port);
if(err != KERN_SUCCESS)
{
mach_error("Can't allocate mach port\n", err);
return (-1);
}
err = mach_port_insert_right(mach_task_self (),
port,
port,
MACH_MSG_TYPE_MAKE_SEND);
if(err != KERN_SUCCESS)
{
mach_error("Can't insert port right\n", err);
return (-1);
}
(*recv_port) = port;
return (0);
}
pid_t
fork_pass_port(mach_port_t pass_port, int32_t (*child_start)(mach_port_t port, void *arg), void *arg)
{
pid_t pid = 0;
int32_t rtrn = 0;
kern_return_t err;
mach_port_t special_port = MACH_PORT_NULL;
/* Setup the mach port. */
if(setup_recv_port(&pass_port) != 0)
{
printf("Can't setup mach port\n");
return (-1);
}
/* Grab our current task's(process's) HOST_NAME special port. */
err = task_get_special_port(mach_task_self(), TASK_HOST_PORT, &special_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't get special port:\n", err);
return (-1);
}
/* Set the HOST_NAME special port as the parent recv port. */
err = task_set_special_port(mach_task_self(), TASK_HOST_PORT, pass_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't set special port:\n", err);
return (-1);
}
pid = fork();
if(pid == 0)
{
mach_port_t host_port = MACH_PORT_NULL;
mach_port_t port = MACH_PORT_NULL;
/* In the child process grab the port passed by the parent. */
err = task_get_special_port(mach_task_self(), TASK_HOST_PORT, &pass_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't get special port:\n", err);
return (-1);
}
/* Create a port with a send right. */
if(setup_recv_port(&port) != 0)
{
printf("Can't setup mach port\n");
return (-1);
}
/* Send port to parent. */
rtrn = send_port(pass_port, port);
if(rtrn < 0)
{
printf("Can't send port\n");
return (-1);
}
/* Now that were done passing the mach port, start the function passed by the caller. */
child_start(pass_port, arg);
/* Exit and clean up the child process. */
_exit(0);
}
else if(pid > 0)
{
mach_port_t child_port = MACH_PORT_NULL;
rtrn = recv_port(pass_port, &child_port);
if(rtrn < 0)
{
printf("Can't recv port\n");
return (-1);
}
/* Reset parents special port. */
err = task_set_special_port(mach_task_self(), TASK_HOST_PORT, special_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't set special port:\n", err);
return (-1);
}
return (0);
}
else
{
/* Error, so cleanup the mach port. */
err = mach_port_deallocate(mach_task_self(), pass_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't deallocate mach port\n", err);
return (-1);
}
perror("fork");
return (-1);
}
}
static int32_t start(mach_port_t port, void *arg)
{
printf("Started\n");
return (0);
}
int main(void)
{
char *arg = "argument";
mach_port_t port = MACH_PORT_NULL;
pid_t pid = fork_pass_port(port, start, arg);
if(pid < 0)
{
printf("Can't fork and pass msg port\n");
return (-1);
}
return (0);
}
任何给定端口都只有一个接收权。 parent 对其创建的端口具有接收权。特殊端口的继承仅针对发送权限。所以,child只继承你通讯端口的发送权。
您链接到的文章暗示 child 通过此端口向 parent 发送消息。 child 应该已经创建了自己的具有接收权限的新端口。它的消息将携带该端口的发送或 send-once 权利到 parent,具体取决于您是否需要正在进行的双工通信。 child 会将 send(-once) 放在消息的 msgh_local_port
中; parent 将在 msgh_remote_port
中收到它。 parent 可以使用该发送(-一次)权限回复,回复可以携带发送到原始主机端口的权限。 child 可以使用它来恢复其主机端口。
或者,您可以这样做:
- 传递给child,通过特殊的端口继承,parent的任务端口的发送权;有了这个,child 几乎可以对 parent 做任何事情
- 让 child 使用
mach_port_extract_right()
从 parent 中提取您的通信端口的接收权
- 让它取消分配parent任务端口的发送权,只是为了安全
还可以提取原主机端口的发送权,而不是通过IPC接收,应该更简单。
综上所述,是什么让您认为以这种方式使用主机端口比 bootstrap 端口更安全?
我想出了如何通过特殊端口继承来传递 mach 端口。在调用 fork 之前,您必须暂时将 TASK_BOOTSTRAP_PORT
替换为您要通过的端口。其他特殊端口以某种方式失败。下面是 "port swap dance".
请注意,此代码仅在 OSX 10.11.3 上测试过,可能不适用于 OSX 的先前或未来版本。
#include <stdio.h>
#include <mach/mach.h>
#include <mach/error.h>
#include <mach/message.h>
#include <unistd.h>
#define SPECIAL_PORT TASK_BOOTSTRAP_PORT
static int32_t
send_port(mach_port_t remote_port, mach_port_t port)
{
kern_return_t err;
struct
{
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t task_port;
} msg;
msg.header.msgh_remote_port = remote_port;
msg.header.msgh_local_port = MACH_PORT_NULL;
msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0) |
MACH_MSGH_BITS_COMPLEX;
msg.header.msgh_size = sizeof msg;
msg.body.msgh_descriptor_count = 1;
msg.task_port.name = port;
msg.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
msg.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
err = mach_msg_send(&msg.header);
if(err != KERN_SUCCESS)
{
mach_error("Can't send mach msg\n", err);
return (-1);
}
return (0);
}
static int32_t
recv_port(mach_port_t recv_port, mach_port_t *port)
{
kern_return_t err;
struct
{
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t task_port;
mach_msg_trailer_t trailer;
} msg;
err = mach_msg(&msg.header, MACH_RCV_MSG,
0, sizeof msg, recv_port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if(err != KERN_SUCCESS)
{
mach_error("Can't recieve mach message\n", err);
return (-1);
}
(*port) = msg.task_port.name;
return 0;
}
static int32_t
setup_recv_port(mach_port_t *recv_port)
{
kern_return_t err;
mach_port_t port = MACH_PORT_NULL;
err = mach_port_allocate(mach_task_self (),
MACH_PORT_RIGHT_RECEIVE, &port);
if(err != KERN_SUCCESS)
{
mach_error("Can't allocate mach port\n", err);
return (-1);
}
err = mach_port_insert_right(mach_task_self (),
port,
port,
MACH_MSG_TYPE_MAKE_SEND);
if(err != KERN_SUCCESS)
{
mach_error("Can't insert port right\n", err);
return (-1);
}
(*recv_port) = port;
return (0);
}
static int32_t
start(mach_port_t port, void *arg)
{
return (0);
}
static pid_t
fork_pass_port(mach_port_t *pass_port,
int32_t (*child_start)(mach_port_t port, void *arg),
void *arg)
{
pid_t pid = 0;
int32_t rtrn = 0;
kern_return_t err;
mach_port_t special_port = MACH_PORT_NULL;
/* Allocate the mach port. */
if(setup_recv_port(pass_port) != 0)
{
printf("Can't setup mach port\n");
return (-1);
}
/* Grab our current process's bootstrap port. */
err = task_get_special_port(mach_task_self(), SPECIAL_PORT, &special_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't get special port:\n", err);
return (-1);
}
/* Set the special port as the parent recv port. */
err = task_set_special_port(mach_task_self(), SPECIAL_PORT, (*pass_port));
if(err != KERN_SUCCESS)
{
mach_error("Can't set special port:\n", err);
return (-1);
}
pid = fork();
if(pid == 0)
{
mach_port_t bootstrap_port = MACH_PORT_NULL;
mach_port_t port = MACH_PORT_NULL;
/* In the child process grab the port passed by the parent. */
err = task_get_special_port(mach_task_self(), SPECIAL_PORT, pass_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't get special port:\n", err);
return (-1);
}
/* Create a port with a send right. */
if(setup_recv_port(&port) != 0)
{
printf("Can't setup mach port\n");
return (-1);
}
/* Send port to parent. */
rtrn = send_port((*pass_port), port);
if(rtrn < 0)
{
printf("Can't send port\n");
return (-1);
}
/* Receive the real bootstrap port from the parent. */
rtrn = recv_port(port, &bootstrap_port);
if(rtrn < 0)
{
printf("Can't receive bootstrap port\n");
return (-1);
}
/* Set the bootstrap port back to normal. */
err = task_set_special_port(mach_task_self(), SPECIAL_PORT, bootstrap_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't set special port:\n", err);
return (-1);
}
/* Now that were done with the port dance, start the function passed by the caller. */
child_start((*pass_port), arg);
/* Exit and clean up the child process. */
_exit(0);
}
else if(pid > 0)
{
mach_port_t child_port = MACH_PORT_NULL;
/* Grab the child's recv port. */
rtrn = recv_port((*pass_port), &child_port);
if(rtrn < 0)
{
printf("Can't recv port\n");
return (-1);
}
/* Send the child the original bootstrap port. */
rtrn = send_port(child_port, special_port);
if(rtrn < 0)
{
printf("Can't send bootstrap port\n");
return (-1);
}
/* Reset parents special port. */
err = task_set_special_port(mach_task_self(), SPECIAL_PORT, special_port);
if(err != KERN_SUCCESS)
{
mach_error("Can't set special port:\n", err);
return (-1);
}
return (0);
}
else
{
/* Error, so cleanup the mach port. */
err = mach_port_deallocate(mach_task_self(), (*pass_port));
if(err != KERN_SUCCESS)
{
mach_error("Can't deallocate mach port\n", err);
return (-1);
}
perror("fork");
return (-1);
}
}
int main(void)
{
/* Argument to pass to the child process. */
char *arg = "argument";
/* Mach port we want to pass to the child. */
mach_port_t port = MACH_PORT_NULL;
pid_t pid = fork_pass_port(&port, start, arg);
if(pid < 0)
{
printf("Can't fork and pass msg port\n");
return (-1);
}
return (0);
}