ipc_channel/platform/macos/
mod.rs

1// Copyright 2015 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10use self::mach_sys::mach_port_deallocate;
11use self::mach_sys::{kern_return_t, mach_msg_body_t, mach_msg_header_t, mach_msg_return_t};
12use self::mach_sys::{mach_msg_ool_descriptor_t, mach_msg_port_descriptor_t, mach_msg_type_name_t};
13use self::mach_sys::{mach_msg_timeout_t, mach_port_limits_t, mach_port_msgcount_t};
14use self::mach_sys::{mach_port_right_t, mach_port_t, mach_task_self_, vm_inherit_t};
15use crate::ipc::{self, IpcMessage};
16
17use bincode;
18use lazy_static::lazy_static;
19use libc::{self, c_char, c_uint, c_void, size_t};
20use rand::{self, Rng};
21use std::cell::Cell;
22use std::convert::TryInto;
23use std::error::Error as StdError;
24use std::ffi::CString;
25use std::fmt::{self, Debug, Formatter};
26use std::io;
27use std::marker::PhantomData;
28use std::mem;
29use std::ops::Deref;
30use std::ptr;
31use std::slice;
32use std::sync::RwLock;
33use std::time::Duration;
34
35mod mach_sys;
36
37/// The size that we preallocate on the stack to receive messages. If the message is larger than
38/// this, we retry and spill to the heap.
39const SMALL_MESSAGE_SIZE: usize = 4096;
40
41/// A string to prepend to our bootstrap ports.
42static BOOTSTRAP_PREFIX: &str = "org.rust-lang.ipc-channel.";
43
44const BOOTSTRAP_NAME_IN_USE: kern_return_t = 1101;
45const BOOTSTRAP_SUCCESS: kern_return_t = 0;
46const KERN_NOT_IN_SET: kern_return_t = 12;
47const KERN_INVALID_NAME: kern_return_t = 15;
48const KERN_INVALID_RIGHT: kern_return_t = 17;
49const KERN_INVALID_VALUE: kern_return_t = 18;
50const KERN_UREFS_OVERFLOW: kern_return_t = 19;
51const KERN_INVALID_CAPABILITY: kern_return_t = 20;
52const KERN_SUCCESS: kern_return_t = 0;
53const KERN_NO_SPACE: kern_return_t = 3;
54const MACH_MSGH_BITS_COMPLEX: u32 = 0x80000000;
55const MACH_MSG_IPC_KERNEL: kern_return_t = 0x00000800;
56const MACH_MSG_IPC_SPACE: kern_return_t = 0x00002000;
57const MACH_MSG_OOL_DESCRIPTOR: u32 = 1;
58const MACH_MSG_PORT_DESCRIPTOR: u32 = 0;
59const MACH_MSG_SUCCESS: kern_return_t = 0;
60const MACH_MSG_TIMEOUT_NONE: mach_msg_timeout_t = 0;
61const MACH_MSG_TYPE_COPY_SEND: u8 = 19;
62const MACH_MSG_TYPE_MAKE_SEND: u8 = 20;
63const MACH_MSG_TYPE_MAKE_SEND_ONCE: u8 = 21;
64const MACH_MSG_TYPE_MOVE_RECEIVE: u32 = 16;
65const MACH_MSG_TYPE_MOVE_SEND: u32 = 17;
66const MACH_MSG_TYPE_PORT_SEND: u32 = MACH_MSG_TYPE_MOVE_SEND;
67const MACH_MSG_VIRTUAL_COPY: c_uint = 1;
68const MACH_MSG_VM_KERNEL: kern_return_t = 0x00000400;
69const MACH_MSG_VM_SPACE: kern_return_t = 0x00001000;
70const MACH_NOTIFY_FIRST: i32 = 64;
71const MACH_NOTIFY_NO_SENDERS: i32 = MACH_NOTIFY_FIRST + 6;
72const MACH_PORT_LIMITS_INFO: i32 = 1;
73const MACH_PORT_NULL: mach_port_t = 0;
74const MACH_PORT_QLIMIT_LARGE: mach_port_msgcount_t = 1024;
75const MACH_PORT_QLIMIT_MAX: mach_port_msgcount_t = MACH_PORT_QLIMIT_LARGE;
76const MACH_PORT_RIGHT_PORT_SET: mach_port_right_t = 3;
77const MACH_PORT_RIGHT_RECEIVE: mach_port_right_t = 1;
78const MACH_PORT_RIGHT_SEND: mach_port_right_t = 0;
79const MACH_RCV_BODY_ERROR: kern_return_t = 0x1000400c;
80const MACH_RCV_HEADER_ERROR: kern_return_t = 0x1000400b;
81const MACH_RCV_INTERRUPTED: kern_return_t = 0x10004005;
82const MACH_RCV_INVALID_DATA: kern_return_t = 0x10004008;
83const MACH_RCV_INVALID_NAME: kern_return_t = 0x10004002;
84const MACH_RCV_INVALID_NOTIFY: kern_return_t = 0x10004007;
85const MACH_RCV_INVALID_TRAILER: kern_return_t = 0x1000400f;
86const MACH_RCV_INVALID_TYPE: kern_return_t = 0x1000400d;
87const MACH_RCV_IN_PROGRESS: kern_return_t = 0x10004001;
88const MACH_RCV_IN_PROGRESS_TIMED: kern_return_t = 0x10004011;
89const MACH_RCV_IN_SET: kern_return_t = 0x1000400a;
90const MACH_RCV_LARGE: i32 = 4;
91const MACH_RCV_MSG: i32 = 2;
92const MACH_RCV_PORT_CHANGED: kern_return_t = 0x10004006;
93const MACH_RCV_PORT_DIED: kern_return_t = 0x10004009;
94const MACH_RCV_SCATTER_SMALL: kern_return_t = 0x1000400e;
95const MACH_RCV_TIMED_OUT: kern_return_t = 0x10004003;
96const MACH_RCV_TIMEOUT: i32 = 0x100;
97const MACH_RCV_TOO_LARGE: kern_return_t = 0x10004004;
98const MACH_SEND_INTERRUPTED: kern_return_t = 0x10000007;
99const MACH_SEND_INVALID_DATA: kern_return_t = 0x10000002;
100const MACH_SEND_INVALID_DEST: kern_return_t = 0x10000003;
101const MACH_SEND_INVALID_HEADER: kern_return_t = 0x10000010;
102const MACH_SEND_INVALID_MEMORY: kern_return_t = 0x1000000c;
103const MACH_SEND_INVALID_NOTIFY: kern_return_t = 0x1000000b;
104const MACH_SEND_INVALID_REPLY: kern_return_t = 0x10000009;
105const MACH_SEND_INVALID_RIGHT: kern_return_t = 0x1000000a;
106const MACH_SEND_INVALID_RT_OOL_SIZE: kern_return_t = 0x10000015;
107const MACH_SEND_INVALID_TRAILER: kern_return_t = 0x10000011;
108const MACH_SEND_INVALID_TYPE: kern_return_t = 0x1000000f;
109const MACH_SEND_INVALID_VOUCHER: kern_return_t = 0x10000005;
110const MACH_SEND_IN_PROGRESS: kern_return_t = 0x10000001;
111const MACH_SEND_MSG: i32 = 1;
112const MACH_SEND_MSG_TOO_SMALL: kern_return_t = 0x10000008;
113const MACH_SEND_NO_BUFFER: kern_return_t = 0x1000000d;
114const MACH_SEND_TIMED_OUT: kern_return_t = 0x10000004;
115const MACH_SEND_TOO_LARGE: kern_return_t = 0x1000000e;
116const TASK_BOOTSTRAP_PORT: i32 = 4;
117const VM_INHERIT_SHARE: vm_inherit_t = 0;
118
119#[allow(non_camel_case_types)]
120type name_t = *const c_char;
121
122pub fn channel() -> Result<(OsIpcSender, OsIpcReceiver), MachError> {
123    let receiver = OsIpcReceiver::new()?;
124    let sender = receiver.sender()?;
125    receiver.request_no_senders_notification()?;
126    Ok((sender, receiver))
127}
128
129#[derive(PartialEq, Debug)]
130pub struct OsIpcReceiver {
131    port: Cell<mach_port_t>,
132}
133
134impl Drop for OsIpcReceiver {
135    fn drop(&mut self) {
136        let port = self.port.get();
137        if port != MACH_PORT_NULL {
138            mach_port_mod_release(port, MACH_PORT_RIGHT_RECEIVE).unwrap();
139        }
140    }
141}
142
143fn mach_port_allocate(right: mach_port_right_t) -> Result<mach_port_t, KernelError> {
144    let mut port: mach_port_t = 0;
145    let os_result = unsafe { mach_sys::mach_port_allocate(mach_task_self(), right, &mut port) };
146    if os_result == KERN_SUCCESS {
147        return Ok(port);
148    }
149    Err(os_result.into())
150}
151
152fn mach_port_mod_addref(port: mach_port_t, right: mach_port_right_t) -> Result<(), KernelError> {
153    let err = unsafe { mach_sys::mach_port_mod_refs(mach_task_self(), port, right, 1) };
154    if err == KERN_SUCCESS {
155        return Ok(());
156    }
157    Err(err.into())
158}
159
160fn mach_port_mod_release(port: mach_port_t, right: mach_port_right_t) -> Result<(), KernelError> {
161    let err = unsafe { mach_sys::mach_port_mod_refs(mach_task_self(), port, right, -1) };
162    if err == KERN_SUCCESS {
163        return Ok(());
164    }
165    Err(err.into())
166}
167
168fn mach_port_move_member(port: mach_port_t, set: mach_port_t) -> Result<(), KernelError> {
169    let error = unsafe { mach_sys::mach_port_move_member(mach_task_self(), port, set) };
170    if error == KERN_SUCCESS {
171        return Ok(());
172    }
173    Err(error.into())
174}
175
176fn mach_port_extract_right(
177    port: mach_port_t,
178    message_type: mach_msg_type_name_t,
179) -> Result<(mach_port_t, mach_msg_type_name_t), KernelError> {
180    let (mut right, mut acquired_right) = (0, 0);
181    let error = unsafe {
182        mach_sys::mach_port_extract_right(
183            mach_task_self(),
184            port,
185            message_type,
186            &mut right,
187            &mut acquired_right,
188        )
189    };
190    if error == KERN_SUCCESS {
191        return Ok((right, acquired_right));
192    }
193    Err(error.into())
194}
195
196impl OsIpcReceiver {
197    fn new() -> Result<OsIpcReceiver, MachError> {
198        let port = mach_port_allocate(MACH_PORT_RIGHT_RECEIVE)?;
199        let limits = mach_port_limits_t {
200            mpl_qlimit: MACH_PORT_QLIMIT_MAX,
201        };
202        let os_result = unsafe {
203            mach_sys::mach_port_set_attributes(
204                mach_task_self(),
205                port,
206                MACH_PORT_LIMITS_INFO,
207                &limits as *const mach_port_limits_t as *mut i32,
208                1,
209            )
210        };
211        if os_result == KERN_SUCCESS {
212            Ok(OsIpcReceiver::from_name(port))
213        } else {
214            Err(KernelError::from(os_result).into())
215        }
216    }
217
218    fn from_name(port: mach_port_t) -> OsIpcReceiver {
219        OsIpcReceiver {
220            port: Cell::new(port),
221        }
222    }
223
224    fn extract_port(&self) -> mach_port_t {
225        let port = self.port.get();
226        debug_assert!(port != MACH_PORT_NULL);
227        port
228    }
229
230    fn consume_port(&self) -> mach_port_t {
231        let port = self.extract_port();
232        self.port.set(MACH_PORT_NULL);
233        port
234    }
235
236    pub fn consume(&self) -> OsIpcReceiver {
237        OsIpcReceiver::from_name(self.consume_port())
238    }
239
240    fn sender(&self) -> Result<OsIpcSender, MachError> {
241        let port = self.port.get();
242        debug_assert!(port != MACH_PORT_NULL);
243        let (right, acquired_right) =
244            mach_port_extract_right(port, MACH_MSG_TYPE_MAKE_SEND as u32)?;
245        debug_assert!(acquired_right == MACH_MSG_TYPE_PORT_SEND);
246        Ok(OsIpcSender::from_name(right))
247    }
248
249    fn register_bootstrap_name(&self) -> Result<(u32, String), MachError> {
250        let port = self.port.get();
251        debug_assert!(port != MACH_PORT_NULL);
252        unsafe {
253            let mut bootstrap_port = 0;
254            let os_result = mach_sys::task_get_special_port(
255                mach_task_self(),
256                TASK_BOOTSTRAP_PORT,
257                &mut bootstrap_port,
258            );
259            if os_result != KERN_SUCCESS {
260                return Err(KernelError::from(os_result).into());
261            }
262
263            let (right, acquired_right) =
264                mach_port_extract_right(port, MACH_MSG_TYPE_MAKE_SEND as u32)?;
265            debug_assert!(acquired_right == MACH_MSG_TYPE_PORT_SEND);
266
267            let mut os_result;
268            let mut name;
269            loop {
270                name = format!("{}{}", BOOTSTRAP_PREFIX, rand::thread_rng().gen::<i64>());
271                let c_name = CString::new(name.clone()).unwrap();
272                os_result = bootstrap_register2(bootstrap_port, c_name.as_ptr(), right, 0);
273                if os_result == BOOTSTRAP_NAME_IN_USE {
274                    continue;
275                }
276                if os_result != BOOTSTRAP_SUCCESS {
277                    return Err(MachError::from(os_result));
278                }
279                break;
280            }
281            Ok((right, name))
282        }
283    }
284
285    fn unregister_global_name(name: String) -> Result<(), MachError> {
286        unsafe {
287            let mut bootstrap_port = 0;
288            let os_result = mach_sys::task_get_special_port(
289                mach_task_self(),
290                TASK_BOOTSTRAP_PORT,
291                &mut bootstrap_port,
292            );
293            if os_result != KERN_SUCCESS {
294                return Err(KernelError::from(os_result).into());
295            }
296
297            let c_name = CString::new(name).unwrap();
298            let os_result = bootstrap_register2(bootstrap_port, c_name.as_ptr(), MACH_PORT_NULL, 0);
299            if os_result == BOOTSTRAP_SUCCESS {
300                Ok(())
301            } else {
302                Err(MachError::from(os_result))
303            }
304        }
305    }
306
307    fn request_no_senders_notification(&self) -> Result<(), MachError> {
308        let port = self.port.get();
309        debug_assert!(port != MACH_PORT_NULL);
310        unsafe {
311            let os_result = mach_sys::mach_port_request_notification(
312                mach_task_self(),
313                port,
314                MACH_NOTIFY_NO_SENDERS,
315                0,
316                port,
317                MACH_MSG_TYPE_MAKE_SEND_ONCE as u32,
318                &mut 0,
319            );
320            if os_result != KERN_SUCCESS {
321                return Err(KernelError::from(os_result).into());
322            }
323        }
324        Ok(())
325    }
326
327    fn recv_with_blocking_mode(
328        &self,
329        blocking_mode: BlockingMode,
330    ) -> Result<IpcMessage, MachError> {
331        select(self.port.get(), blocking_mode).and_then(|result| match result {
332            OsIpcSelectionResult::DataReceived(_, ipc_message) => Ok(ipc_message),
333            OsIpcSelectionResult::ChannelClosed(_) => Err(MachError::from(MACH_NOTIFY_NO_SENDERS)),
334        })
335    }
336
337    pub fn recv(&self) -> Result<IpcMessage, MachError> {
338        self.recv_with_blocking_mode(BlockingMode::Blocking)
339    }
340
341    pub fn try_recv(&self) -> Result<IpcMessage, MachError> {
342        self.recv_with_blocking_mode(BlockingMode::Nonblocking)
343    }
344
345    pub fn try_recv_timeout(&self, duration: Duration) -> Result<IpcMessage, MachError> {
346        self.recv_with_blocking_mode(BlockingMode::Timeout(duration))
347    }
348}
349
350enum SendData<'a> {
351    Inline(&'a [u8]),
352    OutOfLine(Option<OsIpcSharedMemory>),
353}
354
355lazy_static! {
356    static ref MAX_INLINE_SIZE: RwLock<usize> = RwLock::new(usize::MAX);
357}
358
359impl<'a> From<&'a [u8]> for SendData<'a> {
360    fn from(data: &'a [u8]) -> SendData<'a> {
361        let max_inline_size = *MAX_INLINE_SIZE.read().unwrap();
362        if data.len() >= max_inline_size {
363            // Convert the data payload into a shared memory region to avoid exceeding
364            // any message size limits.
365            SendData::OutOfLine(Some(OsIpcSharedMemory::from_bytes(data)))
366        } else {
367            SendData::Inline(data)
368        }
369    }
370}
371
372impl<'a> SendData<'a> {
373    fn take_shared_memory(&mut self) -> Option<OsIpcSharedMemory> {
374        match *self {
375            SendData::Inline(_) => None,
376            SendData::OutOfLine(ref mut data) => data.take(),
377        }
378    }
379
380    fn is_inline(&self) -> bool {
381        match *self {
382            SendData::Inline(_) => true,
383            SendData::OutOfLine(_) => false,
384        }
385    }
386
387    fn inline_data(&self) -> &[u8] {
388        match *self {
389            SendData::Inline(data) => data,
390            SendData::OutOfLine(_) => &[],
391        }
392    }
393}
394
395#[derive(PartialEq, Debug)]
396pub struct OsIpcSender {
397    port: mach_port_t,
398    // Make sure this is `!Sync`, to match `crossbeam_channel::Sender`; and to discourage sharing
399    // references.
400    //
401    // (Rather, senders should just be cloned, as they are shared internally anyway --
402    // another layer of sharing only adds unnecessary overhead...)
403    nosync_marker: PhantomData<Cell<()>>,
404}
405
406impl Drop for OsIpcSender {
407    fn drop(&mut self) {
408        if self.port == MACH_PORT_NULL {
409            return;
410        }
411        deallocate_mach_port(self.port);
412    }
413}
414
415impl Clone for OsIpcSender {
416    fn clone(&self) -> OsIpcSender {
417        let mut cloned_port = self.port;
418        if cloned_port != MACH_PORT_NULL {
419            match mach_port_mod_addref(cloned_port, MACH_PORT_RIGHT_SEND) {
420                Ok(()) => (),
421                Err(KernelError::InvalidRight) => cloned_port = MACH_PORT_NULL,
422                Err(error) => panic!("mach_port_mod_refs(1, {}) failed: {:?}", cloned_port, error),
423            }
424        }
425        OsIpcSender {
426            port: cloned_port,
427            nosync_marker: PhantomData,
428        }
429    }
430}
431
432impl OsIpcSender {
433    fn from_name(port: mach_port_t) -> OsIpcSender {
434        OsIpcSender {
435            port,
436            nosync_marker: PhantomData,
437        }
438    }
439
440    pub fn connect(name: String) -> Result<OsIpcSender, MachError> {
441        unsafe {
442            let mut bootstrap_port = 0;
443            let os_result = mach_sys::task_get_special_port(
444                mach_task_self(),
445                TASK_BOOTSTRAP_PORT,
446                &mut bootstrap_port,
447            );
448            if os_result != KERN_SUCCESS {
449                return Err(KernelError::from(os_result).into());
450            }
451
452            let mut port = 0;
453            let c_name = CString::new(name).unwrap();
454            let os_result = bootstrap_look_up(bootstrap_port, c_name.as_ptr(), &mut port);
455            if os_result == BOOTSTRAP_SUCCESS {
456                Ok(OsIpcSender::from_name(port))
457            } else {
458                Err(MachError::from(os_result))
459            }
460        }
461    }
462
463    pub fn get_max_fragment_size() -> usize {
464        usize::MAX
465    }
466
467    pub fn send(
468        &self,
469        data: &[u8],
470        ports: Vec<OsIpcChannel>,
471        mut shared_memory_regions: Vec<OsIpcSharedMemory>,
472    ) -> Result<(), MachError> {
473        let mut data = SendData::from(data);
474        if let Some(data) = data.take_shared_memory() {
475            shared_memory_regions.push(data);
476        }
477
478        unsafe {
479            let size = Message::size_of(&data, ports.len(), shared_memory_regions.len());
480            let message = libc::malloc(size as size_t) as *mut Message;
481            (*message).header.msgh_bits = (MACH_MSG_TYPE_COPY_SEND as u32) | MACH_MSGH_BITS_COMPLEX;
482            (*message).header.msgh_size = size as u32;
483            (*message).header.msgh_local_port = MACH_PORT_NULL;
484            (*message).header.msgh_remote_port = self.port;
485            (*message).header.msgh_id = 0;
486            (*message).body.msgh_descriptor_count =
487                (ports.len() + shared_memory_regions.len()) as u32;
488
489            let mut port_descriptor_dest = message.offset(1) as *mut mach_msg_port_descriptor_t;
490            for outgoing_port in &ports {
491                (*port_descriptor_dest).name = outgoing_port.port();
492                (*port_descriptor_dest).pad1 = 0;
493
494                (*port_descriptor_dest).set_disposition(match *outgoing_port {
495                    OsIpcChannel::Sender(_) => MACH_MSG_TYPE_MOVE_SEND,
496                    OsIpcChannel::Receiver(_) => MACH_MSG_TYPE_MOVE_RECEIVE,
497                });
498
499                (*port_descriptor_dest).set_type(MACH_MSG_PORT_DESCRIPTOR);
500                port_descriptor_dest = port_descriptor_dest.offset(1);
501            }
502
503            let mut shared_memory_descriptor_dest =
504                port_descriptor_dest as *mut mach_msg_ool_descriptor_t;
505            for shared_memory_region in &shared_memory_regions {
506                (*shared_memory_descriptor_dest).address =
507                    shared_memory_region.as_ptr() as *const c_void as *mut c_void;
508                (*shared_memory_descriptor_dest).size = shared_memory_region.len() as u32;
509                (*shared_memory_descriptor_dest).set_deallocate(1);
510                (*shared_memory_descriptor_dest).set_copy(MACH_MSG_VIRTUAL_COPY);
511                (*shared_memory_descriptor_dest).set_type(MACH_MSG_OOL_DESCRIPTOR);
512                shared_memory_descriptor_dest = shared_memory_descriptor_dest.offset(1);
513            }
514
515            let is_inline_dest = shared_memory_descriptor_dest as *mut bool;
516            *is_inline_dest = data.is_inline();
517            if data.is_inline() {
518                // Zero out the last word for paranoia's sake.
519                *((message as *mut u8).offset(size as isize - 4) as *mut u32) = 0;
520
521                let data = data.inline_data();
522                let data_size = data.len();
523                let padding_start = is_inline_dest.offset(1) as *mut u8;
524                let padding_count = Message::payload_padding(padding_start as usize);
525                // Zero out padding
526                padding_start.write_bytes(0, padding_count);
527                let data_size_dest = padding_start.add(padding_count) as *mut usize;
528                *data_size_dest = data_size;
529
530                let data_dest = data_size_dest.offset(1) as *mut u8;
531                ptr::copy_nonoverlapping(data.as_ptr(), data_dest, data_size);
532            }
533
534            let os_result = mach_sys::mach_msg(
535                message as *mut _,
536                MACH_SEND_MSG,
537                (*message).header.msgh_size,
538                0,
539                MACH_PORT_NULL,
540                MACH_MSG_TIMEOUT_NONE,
541                MACH_PORT_NULL,
542            );
543            libc::free(message as *mut _);
544            if os_result == MACH_SEND_TOO_LARGE && data.is_inline() {
545                let inline_data = data.inline_data();
546                {
547                    let mut max_inline_size = MAX_INLINE_SIZE.write().unwrap();
548                    let inline_len = inline_data.len();
549                    if inline_len < *max_inline_size {
550                        *max_inline_size = inline_len;
551                    }
552                }
553                return self.send(inline_data, ports, shared_memory_regions);
554            }
555            if os_result != MACH_MSG_SUCCESS {
556                return Err(MachError::from(os_result));
557            }
558            for outgoing_port in ports {
559                mem::forget(outgoing_port);
560            }
561            for shared_memory_region in shared_memory_regions {
562                mem::forget(shared_memory_region);
563            }
564            Ok(())
565        }
566    }
567}
568
569pub enum OsIpcChannel {
570    Sender(OsIpcSender),
571    Receiver(OsIpcReceiver),
572}
573
574impl OsIpcChannel {
575    fn port(&self) -> mach_port_t {
576        match *self {
577            OsIpcChannel::Sender(ref sender) => sender.port,
578            OsIpcChannel::Receiver(ref receiver) => receiver.port.get(),
579        }
580    }
581}
582
583#[derive(PartialEq, Debug)]
584pub struct OsOpaqueIpcChannel {
585    port: mach_port_t,
586}
587
588impl Drop for OsOpaqueIpcChannel {
589    fn drop(&mut self) {
590        // Make sure we don't leak!
591        debug_assert!(self.port == MACH_PORT_NULL);
592    }
593}
594
595impl OsOpaqueIpcChannel {
596    fn from_name(name: mach_port_t) -> OsOpaqueIpcChannel {
597        OsOpaqueIpcChannel { port: name }
598    }
599
600    pub fn to_sender(&mut self) -> OsIpcSender {
601        OsIpcSender {
602            port: mem::replace(&mut self.port, MACH_PORT_NULL),
603            nosync_marker: PhantomData,
604        }
605    }
606
607    pub fn to_receiver(&mut self) -> OsIpcReceiver {
608        OsIpcReceiver::from_name(mem::replace(&mut self.port, MACH_PORT_NULL))
609    }
610}
611
612pub struct OsIpcReceiverSet {
613    port: mach_port_t,
614    ports: Vec<mach_port_t>,
615}
616
617impl OsIpcReceiverSet {
618    pub fn new() -> Result<OsIpcReceiverSet, MachError> {
619        let port = mach_port_allocate(MACH_PORT_RIGHT_PORT_SET)?;
620        Ok(OsIpcReceiverSet {
621            port,
622            ports: vec![],
623        })
624    }
625
626    pub fn add(&mut self, receiver: OsIpcReceiver) -> Result<u64, MachError> {
627        mach_port_move_member(receiver.extract_port(), self.port)?;
628        let receiver_port = receiver.consume_port();
629        self.ports.push(receiver_port);
630        Ok(receiver_port as u64)
631    }
632
633    pub fn select(&mut self) -> Result<Vec<OsIpcSelectionResult>, MachError> {
634        select(self.port, BlockingMode::Blocking).map(|result| vec![result])
635    }
636}
637
638impl Drop for OsIpcReceiverSet {
639    fn drop(&mut self) {
640        for port in &self.ports {
641            mach_port_mod_release(*port, MACH_PORT_RIGHT_RECEIVE).unwrap();
642        }
643        mach_port_mod_release(self.port, MACH_PORT_RIGHT_PORT_SET).unwrap();
644    }
645}
646
647pub enum OsIpcSelectionResult {
648    DataReceived(u64, IpcMessage),
649    ChannelClosed(u64),
650}
651
652impl OsIpcSelectionResult {
653    pub fn unwrap(self) -> (u64, IpcMessage) {
654        match self {
655            OsIpcSelectionResult::DataReceived(id, ipc_message) => (id, ipc_message),
656            OsIpcSelectionResult::ChannelClosed(id) => {
657                panic!("OsIpcSelectionResult::unwrap(): receiver ID {id} was closed!")
658            },
659        }
660    }
661}
662
663#[derive(Copy, Clone)]
664enum BlockingMode {
665    Blocking,
666    Nonblocking,
667    Timeout(Duration),
668}
669
670fn select(
671    port: mach_port_t,
672    blocking_mode: BlockingMode,
673) -> Result<OsIpcSelectionResult, MachError> {
674    debug_assert!(port != MACH_PORT_NULL);
675    unsafe {
676        let mut buffer = [0; SMALL_MESSAGE_SIZE];
677        let mut allocated_buffer = None;
678        setup_receive_buffer(&mut buffer, port);
679        let mut message = &mut buffer[0] as *mut _ as *mut Message;
680        let (flags, timeout) = match blocking_mode {
681            BlockingMode::Blocking => (MACH_RCV_MSG | MACH_RCV_LARGE, MACH_MSG_TIMEOUT_NONE),
682            BlockingMode::Nonblocking => (MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_TIMEOUT, 0),
683            BlockingMode::Timeout(duration) => duration
684                .as_millis()
685                .try_into()
686                .map(|ms| (MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_TIMEOUT, ms))
687                .unwrap_or((MACH_RCV_MSG | MACH_RCV_LARGE, MACH_MSG_TIMEOUT_NONE)),
688        };
689        match mach_sys::mach_msg(
690            message as *mut _,
691            flags,
692            0,
693            (*message).header.msgh_size,
694            port,
695            timeout,
696            MACH_PORT_NULL,
697        ) {
698            MACH_RCV_TOO_LARGE => {
699                let max_trailer_size =
700                    mem::size_of::<mach_sys::mach_msg_max_trailer_t>() as mach_sys::mach_msg_size_t;
701                // the actual size gets written into msgh_size by the kernel
702                let mut actual_size = (*message).header.msgh_size + max_trailer_size;
703                loop {
704                    allocated_buffer = Some(libc::malloc(actual_size as size_t));
705                    setup_receive_buffer(
706                        slice::from_raw_parts_mut(
707                            allocated_buffer.unwrap() as *mut u8,
708                            actual_size as usize,
709                        ),
710                        port,
711                    );
712                    message = allocated_buffer.unwrap() as *mut Message;
713                    match mach_sys::mach_msg(
714                        message as *mut _,
715                        flags,
716                        0,
717                        actual_size,
718                        port,
719                        timeout,
720                        MACH_PORT_NULL,
721                    ) {
722                        MACH_MSG_SUCCESS => break,
723                        MACH_RCV_TOO_LARGE => {
724                            actual_size = (*message).header.msgh_size + max_trailer_size;
725                            libc::free(allocated_buffer.unwrap() as *mut _);
726                            continue;
727                        },
728                        os_result => {
729                            libc::free(allocated_buffer.unwrap() as *mut _);
730                            return Err(MachError::from(os_result));
731                        },
732                    }
733                }
734            },
735            MACH_MSG_SUCCESS => {},
736            os_result => return Err(MachError::from(os_result)),
737        }
738
739        let local_port = (*message).header.msgh_local_port;
740        if (*message).header.msgh_id == MACH_NOTIFY_NO_SENDERS {
741            return Ok(OsIpcSelectionResult::ChannelClosed(local_port as u64));
742        }
743
744        let (mut ports, mut shared_memory_regions) = (Vec::new(), Vec::new());
745        let mut port_descriptor = message.offset(1) as *mut mach_msg_port_descriptor_t;
746        let mut descriptors_remaining = (*message).body.msgh_descriptor_count;
747        while descriptors_remaining > 0 {
748            if (*port_descriptor).type_() != MACH_MSG_PORT_DESCRIPTOR {
749                break;
750            }
751            ports.push(OsOpaqueIpcChannel::from_name((*port_descriptor).name));
752            port_descriptor = port_descriptor.offset(1);
753            descriptors_remaining -= 1;
754        }
755
756        let mut shared_memory_descriptor = port_descriptor as *mut mach_msg_ool_descriptor_t;
757        while descriptors_remaining > 0 {
758            debug_assert!((*shared_memory_descriptor).type_() == MACH_MSG_OOL_DESCRIPTOR);
759            shared_memory_regions.push(OsIpcSharedMemory::from_raw_parts(
760                (*shared_memory_descriptor).address as *mut u8,
761                (*shared_memory_descriptor).size as usize,
762            ));
763            shared_memory_descriptor = shared_memory_descriptor.offset(1);
764            descriptors_remaining -= 1;
765        }
766
767        let has_inline_data_ptr = shared_memory_descriptor as *mut bool;
768        let has_inline_data = *has_inline_data_ptr;
769        let payload = if has_inline_data {
770            let padding_start = has_inline_data_ptr.offset(1) as *mut u8;
771            let padding_count = Message::payload_padding(padding_start as usize);
772            let payload_size_ptr = padding_start.add(padding_count) as *mut usize;
773            let payload_size = *payload_size_ptr;
774            let max_payload_size = message as usize + ((*message).header.msgh_size as usize)
775                - (shared_memory_descriptor as usize);
776            assert!(payload_size <= max_payload_size);
777            let payload_ptr = payload_size_ptr.offset(1) as *mut u8;
778            slice::from_raw_parts(payload_ptr, payload_size).to_vec()
779        } else {
780            let ool_payload = shared_memory_regions
781                .pop()
782                .expect("Missing OOL shared memory region");
783            ool_payload.to_vec()
784        };
785
786        if let Some(allocated_buffer) = allocated_buffer {
787            libc::free(allocated_buffer)
788        }
789
790        Ok(OsIpcSelectionResult::DataReceived(
791            local_port as u64,
792            IpcMessage::new(payload, ports, shared_memory_regions),
793        ))
794    }
795}
796
797pub struct OsIpcOneShotServer {
798    receiver: OsIpcReceiver,
799    name: String,
800    registration_port: u32,
801}
802
803impl Drop for OsIpcOneShotServer {
804    fn drop(&mut self) {
805        let _ = OsIpcReceiver::unregister_global_name(std::mem::take(&mut self.name));
806        deallocate_mach_port(self.registration_port);
807    }
808}
809
810impl OsIpcOneShotServer {
811    pub fn new() -> Result<(OsIpcOneShotServer, String), MachError> {
812        let receiver = OsIpcReceiver::new()?;
813        let (registration_port, name) = receiver.register_bootstrap_name()?;
814        Ok((
815            OsIpcOneShotServer {
816                receiver,
817                name: name.clone(),
818                registration_port,
819            },
820            name,
821        ))
822    }
823
824    pub fn accept(self) -> Result<(OsIpcReceiver, IpcMessage), MachError> {
825        let ipc_message = self.receiver.recv()?;
826        Ok((self.receiver.consume(), ipc_message))
827    }
828}
829
830pub struct OsIpcSharedMemory {
831    ptr: *mut u8,
832    length: usize,
833}
834
835unsafe impl Send for OsIpcSharedMemory {}
836unsafe impl Sync for OsIpcSharedMemory {}
837
838impl Drop for OsIpcSharedMemory {
839    fn drop(&mut self) {
840        if !self.ptr.is_null() {
841            unsafe {
842                assert!(
843                    mach_sys::vm_deallocate(mach_task_self(), self.ptr as usize, self.length)
844                        == KERN_SUCCESS
845                );
846            }
847        }
848    }
849}
850
851impl Clone for OsIpcSharedMemory {
852    fn clone(&self) -> OsIpcSharedMemory {
853        let mut address = 0;
854        unsafe {
855            if !self.ptr.is_null() {
856                let err = mach_sys::vm_remap(
857                    mach_task_self(),
858                    &mut address,
859                    self.length,
860                    0,
861                    1,
862                    mach_task_self(),
863                    self.ptr as usize,
864                    0,
865                    &mut 0,
866                    &mut 0,
867                    VM_INHERIT_SHARE,
868                );
869                assert!(err == KERN_SUCCESS);
870            }
871            OsIpcSharedMemory::from_raw_parts(address as *mut u8, self.length)
872        }
873    }
874}
875
876impl PartialEq for OsIpcSharedMemory {
877    fn eq(&self, other: &OsIpcSharedMemory) -> bool {
878        **self == **other
879    }
880}
881
882impl Debug for OsIpcSharedMemory {
883    fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
884        (**self).fmt(formatter)
885    }
886}
887
888impl Deref for OsIpcSharedMemory {
889    type Target = [u8];
890
891    #[inline]
892    fn deref(&self) -> &[u8] {
893        if self.ptr.is_null() && self.length > 0 {
894            panic!("attempted to access a consumed `OsIpcSharedMemory`")
895        }
896        unsafe { slice::from_raw_parts(self.ptr, self.length) }
897    }
898}
899
900impl OsIpcSharedMemory {
901    unsafe fn from_raw_parts(ptr: *mut u8, length: usize) -> OsIpcSharedMemory {
902        OsIpcSharedMemory { ptr, length }
903    }
904
905    pub fn from_byte(byte: u8, length: usize) -> OsIpcSharedMemory {
906        unsafe {
907            let address = allocate_vm_pages(length);
908            for element in slice::from_raw_parts_mut(address, length) {
909                *element = byte;
910            }
911            OsIpcSharedMemory::from_raw_parts(address, length)
912        }
913    }
914
915    pub fn from_bytes(bytes: &[u8]) -> OsIpcSharedMemory {
916        unsafe {
917            let address = allocate_vm_pages(bytes.len());
918            ptr::copy_nonoverlapping(bytes.as_ptr(), address, bytes.len());
919            OsIpcSharedMemory::from_raw_parts(address, bytes.len())
920        }
921    }
922}
923
924unsafe fn allocate_vm_pages(length: usize) -> *mut u8 {
925    let mut address = 0;
926    let result = mach_sys::vm_allocate(mach_task_self(), &mut address, length, 1);
927    if result != KERN_SUCCESS {
928        panic!("`vm_allocate()` failed: {}", result);
929    }
930    address as *mut u8
931}
932
933unsafe fn setup_receive_buffer(buffer: &mut [u8], port_name: mach_port_t) {
934    let message = buffer.as_mut_ptr() as *mut mach_msg_header_t;
935    (*message).msgh_local_port = port_name;
936    (*message).msgh_size = buffer.len() as u32
937}
938
939unsafe fn mach_task_self() -> mach_port_t {
940    mach_task_self_
941}
942
943fn deallocate_mach_port(port: mach_port_t) {
944    // mach_port_deallocate and mach_port_mod_refs are very similar, except that
945    // mach_port_mod_refs returns an error when there are no receivers for the port,
946    // causing the sender port to never be deallocated. mach_port_deallocate handles
947    // this case correctly and is therefore important to avoid dangling port leaks.
948    let err = unsafe { mach_port_deallocate(mach_task_self(), port) };
949    if err != KERN_SUCCESS {
950        panic!("mach_port_deallocate({}) failed: {:?}", port, err);
951    }
952}
953
954#[repr(C)]
955struct Message {
956    header: mach_msg_header_t,
957    body: mach_msg_body_t,
958}
959
960impl Message {
961    fn payload_padding(unaligned: usize) -> usize {
962        ((unaligned + 7) & !7) - unaligned // 8 byte alignment
963    }
964
965    fn size_of(data: &SendData, port_length: usize, shared_memory_length: usize) -> usize {
966        let mut size = mem::size_of::<Message>()
967            + mem::size_of::<mach_msg_port_descriptor_t>() * port_length
968            + mem::size_of::<mach_msg_ool_descriptor_t>() * shared_memory_length
969            + mem::size_of::<bool>();
970
971        if data.is_inline() {
972            // rustc panics in debug mode for unaligned accesses.
973            // so include padding to start payload at 8-byte aligned address
974            size += Self::payload_padding(size);
975            size += mem::size_of::<usize>() + data.inline_data().len();
976        }
977
978        // Round up to the next 4 bytes; mach_msg_send returns an error for unaligned sizes.
979        if (size & 0x3) != 0 {
980            size = (size & !0x3) + 4;
981        }
982
983        size
984    }
985}
986
987#[derive(Clone, Copy, Debug, PartialEq)]
988pub enum KernelError {
989    Success,
990    NoSpace,
991    InvalidName,
992    InvalidRight,
993    InvalidValue,
994    InvalidCapability,
995    UrefsOverflow,
996    NotInSet,
997    Unknown(kern_return_t),
998}
999
1000impl fmt::Display for KernelError {
1001    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1002        match *self {
1003            KernelError::Success => write!(fmt, "Success."),
1004            KernelError::NoSpace => write!(fmt, "No room in IPC name space for another right."),
1005            KernelError::InvalidName => write!(fmt, "Name doesn't denote a right in the task."),
1006            KernelError::InvalidRight => {
1007                write!(fmt, "Name denotes a right, but not an appropriate right.")
1008            },
1009            KernelError::InvalidValue => write!(fmt, "Blatant range error."),
1010            KernelError::InvalidCapability => {
1011                write!(fmt, "The supplied (port) capability is improper.")
1012            },
1013            KernelError::UrefsOverflow => {
1014                write!(fmt, "Operation would overflow limit on user-references.")
1015            },
1016            KernelError::NotInSet => write!(fmt, "Receive right is not a member of a port set."),
1017            KernelError::Unknown(code) => write!(fmt, "Unknown kernel error: {:x}", code),
1018        }
1019    }
1020}
1021
1022impl StdError for KernelError {}
1023
1024impl From<kern_return_t> for KernelError {
1025    fn from(code: kern_return_t) -> KernelError {
1026        match code {
1027            KERN_SUCCESS => KernelError::Success,
1028            KERN_NO_SPACE => KernelError::NoSpace,
1029            KERN_INVALID_NAME => KernelError::InvalidName,
1030            KERN_INVALID_RIGHT => KernelError::InvalidRight,
1031            KERN_INVALID_VALUE => KernelError::InvalidValue,
1032            KERN_INVALID_CAPABILITY => KernelError::InvalidCapability,
1033            KERN_UREFS_OVERFLOW => KernelError::UrefsOverflow,
1034            KERN_NOT_IN_SET => KernelError::NotInSet,
1035            code => KernelError::Unknown(code),
1036        }
1037    }
1038}
1039
1040#[derive(Clone, Copy, Debug, PartialEq)]
1041pub enum MachError {
1042    Success,
1043    Kernel(KernelError),
1044    IpcSpace,
1045    VmSpace,
1046    IpcKernel,
1047    VmKernel,
1048    RcvInProgress,
1049    RcvInvalidName,
1050    RcvTimedOut,
1051    RcvTooLarge,
1052    RcvInterrupted,
1053    RcvPortChanged,
1054    RcvInvalidNotify,
1055    RcvInvalidData,
1056    RcvPortDied,
1057    RcvInSet,
1058    RcvHeaderError,
1059    RcvBodyError,
1060    RcvInvalidType,
1061    RcvScatterSmall,
1062    RcvInvalidTrailer,
1063    RcvInProgressTimed,
1064    NotifyNoSenders,
1065    SendInterrupted,
1066    SendInvalidData,
1067    SendInvalidDest,
1068    SendInvalidHeader,
1069    SendInvalidMemory,
1070    SendInvalidNotify,
1071    SendInvalidReply,
1072    SendInvalidRight,
1073    SendInvalidRtOolSize,
1074    SendInvalidTrailer,
1075    SendInvalidType,
1076    SendInvalidVoucher,
1077    SendInProgress,
1078    SendMsgTooSmall,
1079    SendNoBuffer,
1080    SendTimedOut,
1081    SendTooLarge,
1082    Unknown(mach_msg_return_t),
1083}
1084
1085impl MachError {
1086    #[allow(dead_code)]
1087    pub fn channel_is_closed(&self) -> bool {
1088        *self == MachError::NotifyNoSenders
1089    }
1090}
1091
1092impl fmt::Display for MachError {
1093    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1094        match *self {
1095            MachError::Success => write!(fmt, "Success"),
1096            MachError::Kernel(kernel_error) => fmt::Display::fmt(&kernel_error, fmt),
1097            MachError::IpcSpace => write!(
1098                fmt,
1099                "No room in IPC name space for another capability name."
1100            ),
1101            MachError::VmSpace => {
1102                write!(fmt, "No room in VM address space for out-of-line memory.")
1103            },
1104            MachError::IpcKernel => {
1105                write!(fmt, "Kernel resource shortage handling an IPC capability.")
1106            },
1107            MachError::VmKernel => {
1108                write!(fmt, "Kernel resource shortage handling out-of-line memory.")
1109            },
1110            MachError::SendInProgress => {
1111                write!(fmt, "Thread is waiting to send.  (Internal use only.)")
1112            },
1113            MachError::SendInvalidData => write!(fmt, "Bogus in-line data."),
1114            MachError::SendInvalidDest => write!(fmt, "Bogus destination port."),
1115            MachError::SendTimedOut => write!(fmt, "Message not sent before timeout expired."),
1116            MachError::SendInvalidVoucher => write!(fmt, "Bogus voucher port."),
1117            MachError::SendInterrupted => write!(fmt, "Software interrupt."),
1118            MachError::SendMsgTooSmall => write!(fmt, "Data doesn't contain a complete message."),
1119            MachError::SendInvalidReply => write!(fmt, "Bogus reply port."),
1120            MachError::SendInvalidRight => write!(fmt, "Bogus port rights in the message body."),
1121            MachError::SendInvalidNotify => write!(fmt, "Bogus notify port argument."),
1122            MachError::SendInvalidMemory => write!(fmt, "Invalid out-of-line memory pointer."),
1123            MachError::SendNoBuffer => write!(fmt, "No message buffer is available."),
1124            MachError::SendTooLarge => write!(fmt, "Send is too large for port"),
1125            MachError::SendInvalidType => write!(fmt, "Invalid msg-type specification."),
1126            MachError::SendInvalidHeader => write!(fmt, "A field in the header had a bad value."),
1127            MachError::SendInvalidTrailer => {
1128                write!(fmt, "The trailer to be sent does not match kernel format.")
1129            },
1130            MachError::SendInvalidRtOolSize => {
1131                write!(fmt, "compatibility: no longer a returned error")
1132            },
1133            MachError::RcvInProgress => {
1134                write!(fmt, "Thread is waiting for receive.  (Internal use only.)")
1135            },
1136            MachError::RcvInvalidName => write!(fmt, "Bogus name for receive port/port-set."),
1137            MachError::RcvTimedOut => write!(fmt, "Didn't get a message within the timeout value."),
1138            MachError::RcvTooLarge => {
1139                write!(fmt, "Message buffer is not large enough for inline data.")
1140            },
1141            MachError::RcvInterrupted => write!(fmt, "Software interrupt."),
1142            MachError::RcvPortChanged => write!(fmt, "compatibility: no longer a returned error"),
1143            MachError::RcvInvalidNotify => write!(fmt, "Bogus notify port argument."),
1144            MachError::RcvInvalidData => write!(fmt, "Bogus message buffer for inline data."),
1145            MachError::RcvPortDied => write!(fmt, "Port/set was sent away/died during receive."),
1146            MachError::RcvInSet => write!(fmt, "compatibility: no longer a returned error"),
1147            MachError::RcvHeaderError => {
1148                write!(fmt, "Error receiving message header.  See special bits.")
1149            },
1150            MachError::RcvBodyError => {
1151                write!(fmt, "Error receiving message body.  See special bits.")
1152            },
1153            MachError::RcvInvalidType => {
1154                write!(fmt, "Invalid msg-type specification in scatter list.")
1155            },
1156            MachError::RcvScatterSmall => {
1157                write!(fmt, "Out-of-line overwrite region is not large enough")
1158            },
1159            MachError::RcvInvalidTrailer => write!(
1160                fmt,
1161                "trailer type or number of trailer elements not supported"
1162            ),
1163            MachError::RcvInProgressTimed => write!(
1164                fmt,
1165                "Waiting for receive with timeout. (Internal use only.)"
1166            ),
1167            MachError::NotifyNoSenders => write!(fmt, "No senders exist for this port."),
1168            MachError::Unknown(mach_error_number) => {
1169                write!(fmt, "Unknown Mach error: {:x}", mach_error_number)
1170            },
1171        }
1172    }
1173}
1174
1175impl StdError for MachError {}
1176
1177impl From<MachError> for bincode::Error {
1178    fn from(mach_error: MachError) -> Self {
1179        io::Error::from(mach_error).into()
1180    }
1181}
1182
1183impl From<mach_msg_return_t> for MachError {
1184    fn from(code: mach_msg_return_t) -> MachError {
1185        match code {
1186            MACH_MSG_SUCCESS => MachError::Success,
1187            MACH_MSG_IPC_KERNEL => MachError::IpcKernel,
1188            MACH_MSG_IPC_SPACE => MachError::IpcSpace,
1189            MACH_MSG_VM_KERNEL => MachError::VmKernel,
1190            MACH_MSG_VM_SPACE => MachError::VmSpace,
1191            MACH_RCV_BODY_ERROR => MachError::RcvBodyError,
1192            MACH_RCV_HEADER_ERROR => MachError::RcvHeaderError,
1193            MACH_RCV_INTERRUPTED => MachError::RcvInterrupted,
1194            MACH_RCV_INVALID_DATA => MachError::RcvInvalidData,
1195            MACH_RCV_INVALID_NAME => MachError::RcvInvalidName,
1196            MACH_RCV_INVALID_NOTIFY => MachError::RcvInvalidNotify,
1197            MACH_RCV_INVALID_TRAILER => MachError::RcvInvalidTrailer,
1198            MACH_RCV_INVALID_TYPE => MachError::RcvInvalidType,
1199            MACH_RCV_IN_PROGRESS => MachError::RcvInProgress,
1200            MACH_RCV_IN_PROGRESS_TIMED => MachError::RcvInProgressTimed,
1201            MACH_RCV_IN_SET => MachError::RcvInSet,
1202            MACH_RCV_PORT_CHANGED => MachError::RcvPortChanged,
1203            MACH_RCV_PORT_DIED => MachError::RcvPortDied,
1204            MACH_RCV_SCATTER_SMALL => MachError::RcvScatterSmall,
1205            MACH_RCV_TIMED_OUT => MachError::RcvTimedOut,
1206            MACH_RCV_TOO_LARGE => MachError::RcvTooLarge,
1207            MACH_NOTIFY_NO_SENDERS => MachError::NotifyNoSenders,
1208            MACH_SEND_INTERRUPTED => MachError::SendInterrupted,
1209            MACH_SEND_INVALID_DATA => MachError::SendInvalidData,
1210            MACH_SEND_INVALID_DEST => MachError::SendInvalidDest,
1211            MACH_SEND_INVALID_HEADER => MachError::SendInvalidHeader,
1212            MACH_SEND_INVALID_MEMORY => MachError::SendInvalidMemory,
1213            MACH_SEND_INVALID_NOTIFY => MachError::SendInvalidNotify,
1214            MACH_SEND_INVALID_REPLY => MachError::SendInvalidReply,
1215            MACH_SEND_INVALID_RIGHT => MachError::SendInvalidRight,
1216            MACH_SEND_INVALID_RT_OOL_SIZE => MachError::SendInvalidRtOolSize,
1217            MACH_SEND_INVALID_TRAILER => MachError::SendInvalidTrailer,
1218            MACH_SEND_INVALID_TYPE => MachError::SendInvalidType,
1219            MACH_SEND_INVALID_VOUCHER => MachError::SendInvalidVoucher,
1220            MACH_SEND_IN_PROGRESS => MachError::SendInProgress,
1221            MACH_SEND_MSG_TOO_SMALL => MachError::SendMsgTooSmall,
1222            MACH_SEND_NO_BUFFER => MachError::SendNoBuffer,
1223            MACH_SEND_TIMED_OUT => MachError::SendTimedOut,
1224            MACH_SEND_TOO_LARGE => MachError::SendTooLarge,
1225            code => MachError::Unknown(code),
1226        }
1227    }
1228}
1229
1230impl From<KernelError> for MachError {
1231    fn from(kernel_error: KernelError) -> MachError {
1232        MachError::Kernel(kernel_error)
1233    }
1234}
1235
1236impl From<MachError> for ipc::TryRecvError {
1237    fn from(error: MachError) -> Self {
1238        match error {
1239            MachError::NotifyNoSenders => ipc::TryRecvError::IpcError(ipc::IpcError::Disconnected),
1240            MachError::RcvTimedOut => ipc::TryRecvError::Empty,
1241            e => ipc::TryRecvError::IpcError(ipc::IpcError::Io(io::Error::from(e))),
1242        }
1243    }
1244}
1245
1246impl From<MachError> for ipc::IpcError {
1247    fn from(error: MachError) -> Self {
1248        match error {
1249            MachError::NotifyNoSenders => ipc::IpcError::Disconnected,
1250            e => ipc::IpcError::Io(io::Error::from(e)),
1251        }
1252    }
1253}
1254
1255impl From<MachError> for io::Error {
1256    /// These error descriptions are from `mach/message.h` and `mach/kern_return.h`.
1257    fn from(mach_error: MachError) -> io::Error {
1258        let kind = match mach_error {
1259            MachError::Success => io::ErrorKind::Other,
1260            MachError::Kernel(KernelError::Success) => io::ErrorKind::Other,
1261            MachError::Kernel(KernelError::NoSpace) => io::ErrorKind::Other,
1262            MachError::Kernel(KernelError::InvalidName) => io::ErrorKind::Other,
1263            MachError::Kernel(KernelError::InvalidRight) => io::ErrorKind::Other,
1264            MachError::Kernel(KernelError::InvalidValue) => io::ErrorKind::Other,
1265            MachError::Kernel(KernelError::InvalidCapability) => io::ErrorKind::Other,
1266            MachError::Kernel(KernelError::UrefsOverflow) => io::ErrorKind::Other,
1267            MachError::Kernel(KernelError::NotInSet) => io::ErrorKind::Other,
1268            MachError::Kernel(KernelError::Unknown(_)) => io::ErrorKind::Other,
1269            MachError::IpcSpace => io::ErrorKind::Other,
1270            MachError::VmSpace => io::ErrorKind::Other,
1271            MachError::IpcKernel => io::ErrorKind::Other,
1272            MachError::VmKernel => io::ErrorKind::Other,
1273            MachError::SendInProgress => io::ErrorKind::Interrupted,
1274            MachError::SendInvalidData => io::ErrorKind::InvalidData,
1275            MachError::SendInvalidDest => io::ErrorKind::NotFound,
1276            MachError::SendTimedOut => io::ErrorKind::TimedOut,
1277            MachError::SendInvalidVoucher => io::ErrorKind::NotFound,
1278            MachError::SendInterrupted => io::ErrorKind::Interrupted,
1279            MachError::SendMsgTooSmall => io::ErrorKind::InvalidData,
1280            MachError::SendInvalidReply => io::ErrorKind::InvalidInput,
1281            MachError::SendInvalidRight => io::ErrorKind::InvalidInput,
1282            MachError::SendInvalidNotify => io::ErrorKind::InvalidInput,
1283            MachError::SendInvalidMemory => io::ErrorKind::InvalidInput,
1284            MachError::SendNoBuffer => io::ErrorKind::Other,
1285            MachError::SendTooLarge => io::ErrorKind::InvalidData,
1286            MachError::SendInvalidType => io::ErrorKind::InvalidInput,
1287            MachError::SendInvalidHeader => io::ErrorKind::InvalidInput,
1288            MachError::SendInvalidTrailer => io::ErrorKind::InvalidData,
1289            MachError::SendInvalidRtOolSize => io::ErrorKind::Other,
1290            MachError::RcvInProgress => io::ErrorKind::Interrupted,
1291            MachError::RcvInvalidName => io::ErrorKind::InvalidInput,
1292            MachError::RcvTimedOut => io::ErrorKind::TimedOut,
1293            MachError::RcvTooLarge => io::ErrorKind::InvalidInput,
1294            MachError::RcvInterrupted => io::ErrorKind::Interrupted,
1295            MachError::RcvPortChanged => io::ErrorKind::Other,
1296            MachError::RcvInvalidNotify => io::ErrorKind::InvalidInput,
1297            MachError::RcvInvalidData => io::ErrorKind::InvalidInput,
1298            MachError::RcvPortDied => io::ErrorKind::BrokenPipe,
1299            MachError::RcvInSet => io::ErrorKind::Other,
1300            MachError::RcvHeaderError => io::ErrorKind::Other,
1301            MachError::RcvBodyError => io::ErrorKind::Other,
1302            MachError::RcvInvalidType => io::ErrorKind::InvalidInput,
1303            MachError::RcvScatterSmall => io::ErrorKind::InvalidInput,
1304            MachError::RcvInvalidTrailer => io::ErrorKind::InvalidInput,
1305            MachError::RcvInProgressTimed => io::ErrorKind::Interrupted,
1306            MachError::NotifyNoSenders => io::ErrorKind::ConnectionReset,
1307            MachError::Unknown(_) => io::ErrorKind::Other,
1308        };
1309        io::Error::new(kind, mach_error)
1310    }
1311}
1312
1313extern "C" {
1314    fn bootstrap_register2(
1315        bp: mach_port_t,
1316        service_name: name_t,
1317        sp: mach_port_t,
1318        flags: u64,
1319    ) -> kern_return_t;
1320    fn bootstrap_look_up(
1321        bp: mach_port_t,
1322        service_name: name_t,
1323        sp: *mut mach_port_t,
1324    ) -> kern_return_t;
1325}