c++ 使用MacOS的Mach消息在以root身份运行的守护进程和另一个非root进程之间进行IPC通信的示例

mzsu5hc0  于 2023-01-28  发布在  Mac
关注(0)|答案(1)|浏览(190)

我试图理解在启动守护进程(以root身份运行)和另一个进程(以user-content运行)之间使用Mach messages进行IPC的底层机制。
假设使用以下数据结构:

struct MACH_MSG_BASE
{
    mach_msg_header_t hdr;
    mach_msg_body_t body;
};

struct MACH_MSG_UINT32
{
    MACH_MSG_BASE base;
    
    unsigned int val;      //Sending this value as a test
};

因此,我在守护进程中运行以下代码:

//Server:

//No error checks for brevity
mach_port_t port = MACH_PORT_NULL;
mach_port_t task = mach_task_self();

mach_port_allocate(task,
                   MACH_PORT_RIGHT_RECEIVE,
                   &port);

mach_port_insert_right(task,
                       port,
                       port,
                       MACH_MSG_TYPE_MAKE_SEND);

MACH_MSG_UINT32 msg = {};
msg.base.hdr.msgh_local_port = port;
msg.base.hdr.msgh_size = sizeof(msg.base);
        
mach_msg(&msg.base.hdr,
         MACH_RCV_MSG,
         0,
         sizeof(msg),
         port,
         MACH_MSG_TIMEOUT_NONE,
         MACH_PORT_NULL);

当我运行上面的代码时,它在mach_msg调用时进入等待模式,正如我所预料的那样。
但是,第一个问题是-如何从另一个进程获取守护进程的端口号?我假设使用task_for_pid,如下所示:

//Client(s):

//No error checks for brevity
mach_port_t port = MACH_PORT_NULL;
mach_port_t task;
task_for_pid(mach_task_self(), server_pid, &task);  //I guess we get server_pid by daemon process name?

mach_port_allocate(task,
                   MACH_PORT_RIGHT_RECEIVE,
                   &port);

MACH_MSG_UINT32 msg = {};
    
msg.base.hdr.msgh_remote_port = _port;
msg.base.hdr.msgh_local_port = MACH_PORT_NULL;
msg.base.hdr.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MAKE_SEND);
msg.base.hdr.msgh_size = sizeof(msg.base);
    
msg.val = 0x12345678;
    
mach_msg(&msg.base.hdr,
         MACH_SEND_MSG,
         sizeof(msg),
         0,
         MACH_PORT_NULL,
         MACH_MSG_TIMEOUT_NONE,
         MACH_PORT_NULL);

但是当我运行上面的代码时,mach_msg返回0x 10000003或MACH_SEND_INVALID_DEST
我哪里做错了?

hgb9j2n6

hgb9j2n61#

task_for_pid几乎从来没有工作这些天,苹果已显着限制它出于安全原因。
查找Mach服务器的方法是让它注册一个端口名,然后让客户端在特权或用户名称空间中查找端口。对于启动守护进程或代理,您可以在launchd plist的MachServices部分指定它提供的端口:

<key>MachServices</key>
<dict>
    <key>com.example.mydaemon.MyMachService1</key>
    <true/>
    <key>com.example.mydaemon.MyMachService2</key>
    <true/>
</dict>

然后,客户端将查找这些端口以使用引导API获得发送权限:

kern_return_t kr = bootstrap_look_up(bootstrap_port, "com.example.mydaemon.MyMachService1", &service_port);

尽管如此,使用低级Mach端口是非常麻烦的。我强烈建议使用XPC,除非你有一些遗留的或系统级的Mach服务需要接口。使用XPC,你仍然可以在launchd plist中注册MachServices,尽管代码端更容易使用。当你使用C++时,你的起点将是xpc_connection_create_mach_service()函数。

相关问题