Skip to main content

mesh_remote/
unix_node.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Unix socket-based mesh node implementation.
5//!
6//! Each pair of nodes communicate using a single, bidirectional Unix socket. On
7//! platforms that support it (Linux), a `SOCK_SEQPACKET` socket is used, which
8//! provides message framing and atomic message sends. Otherwise, a
9//! `SOCK_STREAM` packet is used, and the protocol includes a message size.
10//!
11//! File descriptors are sent between nodes using the `SCM_RIGHTS` functionality
12//! of Unix sockets.
13
14#![cfg(unix)]
15
16#[cfg(target_os = "linux")]
17mod memfd;
18
19use crate::common::InvitationAddress;
20use crate::protocol;
21use futures::FutureExt;
22use futures::StreamExt;
23use futures::channel::mpsc;
24use futures::future;
25use futures::future::BoxFuture;
26use io::ErrorKind;
27use mesh_channel::OneshotReceiver;
28use mesh_channel::OneshotSender;
29use mesh_channel::RecvError;
30use mesh_channel::channel;
31use mesh_channel::oneshot;
32use mesh_node::common::Address;
33use mesh_node::common::NodeId;
34use mesh_node::common::PortId;
35use mesh_node::local_node::Connect;
36use mesh_node::local_node::LocalNode;
37use mesh_node::local_node::OutgoingEvent;
38use mesh_node::local_node::Port;
39use mesh_node::local_node::RemoteNodeHandle;
40use mesh_node::local_node::SendEvent;
41use mesh_node::resource::OsResource;
42use mesh_node::resource::Resource;
43use mesh_protobuf::Protobuf;
44use pal_async::driver::SpawnDriver;
45use pal_async::interest::InterestSlot;
46use pal_async::interest::PollEvents;
47use pal_async::socket::PolledSocket;
48use pal_async::task::Spawn;
49use pal_async::task::Task;
50use parking_lot::Mutex;
51use socket2::Socket;
52use std::collections::HashMap;
53use std::collections::VecDeque;
54use std::fmt::Debug;
55use std::future::Future;
56use std::future::poll_fn;
57use std::io;
58use std::io::IoSlice;
59use std::os::unix::prelude::*;
60use std::pin::pin;
61use std::sync::Arc;
62use thiserror::Error;
63use tracing::instrument;
64use unicycle::FuturesUnordered;
65use zerocopy::FromBytes;
66use zerocopy::FromZeros;
67use zerocopy::IntoBytes;
68
69/// If true, use a SOCK_SEQPACKET socket. Otherwise, use a SOCK_STREAM socket.
70///
71/// SOCK_SEQPACKET is preferred where available because it allows us to avoid
72/// separately tracking message boundaries. Most importantly, this makes it
73/// straightforward to support sending messages from multiple threads
74/// simultaneously.
75const USE_SEQPACKET: bool = cfg!(target_os = "linux");
76
77/// The maximum packet size. Linux uses memfd to send data larger than this.
78/// Other OSes just fail, so choose a larger size.
79///
80/// These values were chosen arbitrarily and have not been tested for
81/// performance.
82const MAX_PACKET_SIZE: usize = if cfg!(target_os = "linux") {
83    0x4000
84} else {
85    0x40000
86};
87
88const MAX_SMALL_EVENT_SIZE: usize = MAX_PACKET_SIZE - size_of::<protocol::PacketHeader>();
89
90/// A node within a mesh that uses Unix sockets to communicate.
91///
92/// Each pairwise connection between two nodes in the mesh communicates via a
93/// pair of bidirectional sockets.
94///
95/// If one node needs to send data to another but does not have a connection, it
96/// sends a request to the leader node to establish a connection. The leader
97/// creates a new socket pair and sends one end to each of the nodes, which the
98/// two nodes can use to communicate.
99pub struct UnixNode {
100    driver: Arc<dyn SpawnDriver>,
101    local_node: Arc<LocalNode>,
102    to_leader: Arc<mesh_channel::Sender<LeaderRequest>>,
103    tasks: Arc<mesh_channel::Sender<SmallTask>>,
104    io_task: Task<()>,
105    // TODO: consider reducing type complexity?
106    leader_resign_send:
107        Mutex<Option<Arc<mesh_channel::Sender<(NodeId, mesh_channel::Sender<Followers>)>>>>,
108
109    // meaningful drop
110    _drop_send: OneshotSender<()>,
111}
112
113/// Error returned when creating a mesh invitation fails.
114#[derive(Debug, Error)]
115#[error("mesh node shut down before invitation could be created")]
116pub struct InviteError;
117
118/// A Clone + Send handle for creating mesh invitations.
119///
120/// Extracted from a [`UnixNode`] via [`UnixNode::inviter()`]. This delegates
121/// invitation requests to the node's leader task via the existing
122/// `LeaderRequest::Invite` channel.
123///
124/// This allows creating invitations from any async task without holding a
125/// reference to the node.
126#[derive(Clone)]
127pub(crate) struct UnixMeshInviter {
128    to_leader: mesh_channel::Sender<LeaderRequest>,
129}
130
131impl UnixMeshInviter {
132    /// Create a mesh invitation for a new node to join.
133    ///
134    /// The returned [`Invitation`] can be sent to another process to allow
135    /// it to join the mesh.
136    pub(crate) async fn invite(&self, port: Port) -> Result<Invitation, InviteError> {
137        let (invitation_send, mut invitation_recv) = channel();
138        self.to_leader
139            .send(LeaderRequest::Invite(port, invitation_send));
140        invitation_recv.recv().await.map_err(|_| InviteError)
141    }
142}
143
144#[derive(Debug, Protobuf)]
145#[mesh(resource = "Resource")]
146enum LeaderRequest {
147    Connect(NodeId),
148    Invite(Port, mesh_channel::Sender<Invitation>),
149}
150
151#[derive(Debug, Protobuf)]
152#[mesh(resource = "Resource")]
153enum FollowerRequest {
154    Connect(
155        NodeId,
156        #[mesh(
157            encoding = "mesh_protobuf::encoding::OptionField<mesh_protobuf::encoding::ResourceField<OwnedFd>>"
158        )]
159        Option<Socket>,
160    ),
161}
162
163#[derive(Debug, Protobuf)]
164#[mesh(resource = "Resource")]
165pub struct Followers {
166    list: Vec<(
167        NodeId,
168        mesh_channel::Receiver<LeaderRequest>,
169        mesh_channel::Sender<FollowerRequest>,
170    )>,
171}
172
173#[derive(Debug, Protobuf)]
174#[mesh(resource = "Resource")]
175struct InitialMessage {
176    leader_send: mesh_channel::Sender<LeaderRequest>,
177    follower_recv: mesh_channel::Receiver<FollowerRequest>,
178    user_port: Port,
179}
180
181/// Processes incoming requests from the leader to a follower. Currently the only
182/// such request is to add a connection to another node.
183#[instrument(skip_all, fields(local_id = ?local_node.id()))]
184async fn run_follower(
185    driver: &dyn SpawnDriver,
186    local_node: &Arc<LocalNode>,
187    mut recv: mesh_channel::Receiver<FollowerRequest>,
188    pending_connections: Arc<Mutex<HashMap<NodeId, RemoteNodeHandle>>>,
189    tasks: &mesh_channel::Sender<SmallTask>,
190) {
191    while let Ok(req) = recv.recv().await {
192        match req {
193            FollowerRequest::Connect(target_id, fd) => {
194                tracing::debug!(?target_id, "got connection request from leader");
195                let handle = pending_connections.lock().remove(&target_id);
196                let handle = handle.unwrap_or_else(|| local_node.get_remote_handle(target_id));
197
198                if let Some(fd) = fd {
199                    start_connection(
200                        tasks,
201                        local_node,
202                        target_id,
203                        handle,
204                        UnixSocket::new(driver, fd),
205                    );
206                } else {
207                    tracing::warn!(?target_id, "leader provided failed connection");
208                }
209            }
210        }
211    }
212}
213
214/// Processes incoming requests from a follower to the leader. Runs until there
215/// are no more followers or until the leader is asked to transfer power to
216/// another node via `resign_recv`.
217#[instrument(skip_all, fields(local_id = ?local_node.id()))]
218async fn run_leader(
219    driver: &dyn SpawnDriver,
220    local_node: &Arc<LocalNode>,
221    mut resign_recv: mesh_channel::Receiver<(NodeId, mesh_channel::Sender<Followers>)>,
222    followers: Followers,
223    tasks: &mesh_channel::Sender<SmallTask>,
224) {
225    let mut senders = HashMap::new();
226    let mut receivers = Vec::new();
227    for (remote_id, recv, send) in followers.list {
228        receivers.push((remote_id, recv));
229        senders.insert(remote_id, send);
230    }
231
232    let new_leader_info = loop {
233        if receivers.is_empty() {
234            return;
235        }
236        let recvs = receivers
237            .iter_mut()
238            .map(|(_, recv)| poll_fn(|cx| recv.poll_recv(cx)));
239        let (req, index, _) = futures::select! { // merge semantics
240            r = resign_recv.next() => break r,
241            r = future::select_all(recvs).fuse() => r,
242        };
243        let remote_id = receivers[index].0;
244        match req {
245            Ok(req) => match req {
246                LeaderRequest::Connect(target_id) => {
247                    tracing::debug!(?target_id, ?remote_id, "connection request");
248                    let remote = senders
249                        .get(&remote_id)
250                        .expect("sender must exist to receive from it");
251                    let mut fd = None;
252                    if let Some(target) = senders.get(&target_id) {
253                        match new_socket_pair() {
254                            Ok((left, right)) => {
255                                tracing::trace!(?target, "send to");
256                                target.send(FollowerRequest::Connect(remote_id, Some(left)));
257                                fd = Some(right);
258                            }
259                            Err(err) => {
260                                tracing::warn!(
261                                    ?target_id,
262                                    ?remote_id,
263                                    error = &err as &dyn std::error::Error,
264                                    "failed to create socket pair for connection request"
265                                );
266                            }
267                        }
268                    } else {
269                        tracing::warn!(?target_id, ?remote_id, "could not find target for remote");
270                    }
271                    remote.send(FollowerRequest::Connect(target_id, fd));
272                }
273                LeaderRequest::Invite(port, send) => {
274                    tracing::debug!(?remote_id, "invitation request");
275                    match new_socket_pair() {
276                        Ok((left, right)) => {
277                            let (leader_send, leader_recv) = channel();
278                            let (follower_send, follower_recv) = channel();
279                            let remote_addr = Address {
280                                node: NodeId::new(),
281                                port: PortId::new(),
282                            };
283                            let local_port_id = PortId::new();
284                            let handle = local_node.add_remote(remote_addr.node);
285                            start_connection(
286                                tasks,
287                                local_node,
288                                remote_addr.node,
289                                handle,
290                                UnixSocket::new(driver, left),
291                            );
292                            let init_send = OneshotSender::<InitialMessage>::from(
293                                local_node.add_port(local_port_id, remote_addr),
294                            );
295                            init_send.send(InitialMessage {
296                                leader_send,
297                                follower_recv,
298                                user_port: port,
299                            });
300                            let invitation = Invitation {
301                                address: InvitationAddress {
302                                    local_addr: remote_addr,
303                                    remote_addr: Address {
304                                        node: local_node.id(),
305                                        port: local_port_id,
306                                    },
307                                },
308                                fd: right.into(),
309                            };
310                            tracing::debug!(
311                                invite_id = ?invitation.address.local_addr.node,
312                                ?remote_id,
313                                "inviting",
314                            );
315                            send.send(invitation);
316                            senders.insert(remote_addr.node, follower_send);
317                            receivers.push((remote_addr.node, leader_recv));
318                        }
319                        Err(err) => {
320                            tracing::error!(
321                                error = &err as &dyn std::error::Error,
322                                "failed to create socket pair",
323                            );
324                        }
325                    }
326                }
327            },
328            Err(err) => {
329                if let RecvError::Error(err) = err {
330                    tracing::debug!(
331                        ?remote_id,
332                        error = &err as &dyn std::error::Error,
333                        "leader connection to remote failed"
334                    );
335                }
336                senders.remove(&remote_id);
337                receivers.swap_remove(index);
338            }
339        }
340    };
341
342    if let Some((new_leader_id, new_leader_followers_sink)) = new_leader_info {
343        if let Some(new_leader_send) = senders.get(&new_leader_id) {
344            tracing::debug!(?new_leader_id, "resigning leadership");
345            // Ensure there is a connection between every follower and the new
346            // leader.
347            for (remote_id, send) in senders.iter() {
348                if new_leader_id != *remote_id {
349                    match new_socket_pair() {
350                        Ok((left, right)) => {
351                            send.send(FollowerRequest::Connect(new_leader_id, Some(left)));
352                            new_leader_send.send(FollowerRequest::Connect(*remote_id, Some(right)));
353                        }
354                        Err(err) => {
355                            tracing::error!(
356                                ?new_leader_id,
357                                error = &err as &dyn std::error::Error,
358                                "failed to connect node to new leader, mesh is leaderless",
359                            );
360                            return;
361                        }
362                    }
363                }
364            }
365
366            // Send all the followers to the new leader.
367            let mut followers = Vec::new();
368            for (remote_id, recv) in receivers {
369                let send = senders
370                    .remove(&remote_id)
371                    .expect("should be in sync with receivers");
372                followers.push((remote_id, recv, send));
373            }
374            new_leader_followers_sink.send(Followers { list: followers });
375        } else {
376            tracing::error!(?new_leader_id, "new leader is unknown, mesh is leaderless");
377        }
378    }
379}
380
381/// A task initiator, implementing by a function returning a future. This is
382/// used to send work to the node's IO thread.
383struct SmallTask {
384    name: &'static str,
385    future: BoxFuture<'static, ()>,
386}
387
388impl Debug for SmallTask {
389    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
390        f.pad("SmallTask")
391    }
392}
393
394impl SmallTask {
395    fn new(name: &'static str, f: impl 'static + Send + Future<Output = ()>) -> Self {
396        Self {
397            name,
398            future: Box::pin(f),
399        }
400    }
401}
402
403/// An invitation allowing another process to join the mesh.
404///
405/// Created by [`UnixNode::invite`].
406#[derive(Debug, Protobuf)]
407#[mesh(resource = "Resource")]
408pub struct Invitation {
409    /// The common invitation addresses.
410    pub address: InvitationAddress,
411    /// The Unix socket used to initiate communications with the mesh.
412    pub fd: OwnedFd,
413}
414
415#[derive(Debug)]
416enum SenderCommand {
417    Send {
418        packet: Vec<u8>,
419        fds: Vec<OsResource>,
420    },
421    ReleaseFds {
422        count: usize,
423    },
424}
425
426#[derive(Clone)]
427struct PacketSender {
428    send: mpsc::UnboundedSender<SenderCommand>,
429    socket: Arc<UnixSocket>,
430}
431
432impl SendEvent for PacketSender {
433    fn event(&self, event: OutgoingEvent<'_>) {
434        let (packet, fds) = match serialize_event(event) {
435            Ok(r) => r,
436            Err(err) => {
437                // FUTURE: fail the port or connection instead?
438                tracing::error!(
439                    error = &err as &dyn std::error::Error,
440                    "failed to serialize event"
441                );
442                return;
443            }
444        };
445
446        // If using SOCK_SEQPACKET, try to send the packet immediately. If this
447        // fails (likely due to EAGAIN), send the packet to the asynchronous
448        // task for deferred processing.
449        //
450        // When SOCK_STREAM is in use, this optimization cannot be tried,
451        // because for stream sockets we may need to issue multiple writes to
452        // send the whole message, and those writes cannot be interleaved
453        // correctly.
454        //
455        // N.B. This can lead to out of order messages. The event protocol is
456        //      responsible for handling this condition.
457        if !USE_SEQPACKET
458            || unix_socket::send_with_fds(
459                self.socket.socket.lock().get().as_fd(),
460                &[IoSlice::new(&packet)],
461                fds.iter().map(|OsResource::Fd(fd)| fd.as_fd()),
462            )
463            .is_err()
464        {
465            let _ = self
466                .send
467                .unbounded_send(SenderCommand::Send { packet, fds });
468        }
469    }
470}
471
472fn serialize_event(event: OutgoingEvent<'_>) -> io::Result<(Vec<u8>, Vec<OsResource>)> {
473    // Serialize the event to a memfd if it's too large to send inline.
474    if event.len() > MAX_SMALL_EVENT_SIZE {
475        return serialize_large_event(event);
476    }
477
478    // Serialize the event to a byte vector.
479    let cap = size_of::<protocol::PacketHeader>() + event.len();
480    let mut packet = Vec::with_capacity(cap);
481    packet.extend_from_slice(
482        protocol::PacketHeader {
483            packet_type: protocol::PacketType::EVENT,
484            reserved: [0; 7],
485        }
486        .as_bytes(),
487    );
488    let mut fds = Vec::new();
489    event.write_to(&mut packet, &mut fds);
490    assert_eq!(packet.len(), cap);
491    Ok((packet, fds))
492}
493
494#[cfg(target_os = "linux")]
495fn serialize_large_event(event: OutgoingEvent<'_>) -> io::Result<(Vec<u8>, Vec<OsResource>)> {
496    let packet = protocol::PacketHeader {
497        packet_type: protocol::PacketType::LARGE_EVENT,
498        ..FromZeros::new_zeroed()
499    }
500    .as_bytes()
501    .to_vec();
502
503    let mut fds = Vec::new();
504
505    let mut memfd = memfd::MemfdBuilder::new(event.len())?;
506    event.write_to(&mut io::Cursor::new(&mut *memfd), &mut fds);
507    fds.insert(0, OsResource::Fd(memfd.seal()?.into()));
508
509    Ok((packet, fds))
510}
511
512#[cfg(not(target_os = "linux"))]
513fn serialize_large_event(_event: OutgoingEvent<'_>) -> io::Result<(Vec<u8>, Vec<OsResource>)> {
514    Err(io::Error::new(
515        ErrorKind::Unsupported,
516        "event too large for this OS",
517    ))
518}
519
520impl Drop for PacketSender {
521    fn drop(&mut self) {
522        // Explicitly close the send channel so that the send task returns, even
523        // though the send channel is also in use by the receive task.
524        self.send.close_channel();
525    }
526}
527
528/// Starts a connection processing task.
529fn start_connection(
530    tasks: &mesh_channel::Sender<SmallTask>,
531    local_node: &Arc<LocalNode>,
532    remote_id: NodeId,
533    handle: RemoteNodeHandle,
534    socket: UnixSocket,
535) {
536    #[expect(clippy::disallowed_methods)] // TODO
537    let (send, recv) = mpsc::unbounded();
538    let socket = Arc::new(socket);
539    let sender = PacketSender {
540        send: send.clone(),
541        socket: socket.clone(),
542    };
543    if handle.connect(sender) {
544        let task = SmallTask::new("run_connection", {
545            let local_node = local_node.clone();
546            run_connection(local_node, remote_id, send, recv, socket, handle)
547        });
548        tasks.send(task);
549        tracing::debug!(?remote_id, "connected");
550    } else {
551        // N.B. This is an expected condition in many scenarios, since the
552        //      leader does not track which connections have already been
553        //      made and so will often send duplicate connection requests.
554        tracing::debug!(?remote_id, "duplicate connection");
555    }
556}
557
558/// Runs the packet processing loop.
559#[instrument(skip_all, fields(local_id = ?local_node.id(), remote_id = ?remote_id))]
560async fn run_connection(
561    local_node: Arc<LocalNode>,
562    remote_id: NodeId,
563    send_send: mpsc::UnboundedSender<SenderCommand>,
564    send_recv: mpsc::UnboundedReceiver<SenderCommand>,
565    socket: Arc<UnixSocket>,
566    handle: RemoteNodeHandle,
567) {
568    let mut retained_fds = VecDeque::new();
569    let mut recv = pin!(
570        async {
571            let r = run_receive(&local_node, &remote_id, &socket, &send_send).await;
572            match &r {
573                Ok(_) => {
574                    tracing::debug!("incoming socket disconnected");
575                }
576                Err(err) => {
577                    tracing::error!(error = err as &dyn std::error::Error, "error receiving");
578                }
579            }
580            r
581        }
582        .fuse()
583    );
584    let mut send = pin!(
585        async {
586            match run_send(send_recv, &socket, &mut retained_fds).await {
587                Ok(_) => {
588                    tracing::debug!("sending is done");
589                }
590                Err(err) => {
591                    tracing::error!(error = &err as &dyn std::error::Error, "failed send");
592                }
593            }
594        }
595        .fuse()
596    );
597    let r = futures::select! { // race semantics
598        r = recv => {
599            // Notify the remote node that no more data will be sent.
600            tracing::trace!("read complete, shutting down writes");
601            let _ = socket.close_write().await;
602            r
603        }
604        _ = send => {
605            match socket.close_write().await {
606                Ok(()) => {
607                    tracing::trace!("shutdown writes, waiting for reads");
608                    recv.await
609                }
610                Err(err) => {
611                    tracing::error!(
612                        error = &err as &dyn std::error::Error,
613                        "failed to shutdown writes, aborting connection",
614                    );
615                    Err(ReceiveError::Io(err))
616                }
617            }
618        }
619    };
620    tracing::trace!("connection done");
621    match r {
622        Ok(()) => handle.disconnect(),
623        Err(err) => handle.fail(err),
624    }
625}
626
627#[derive(Debug, Error)]
628enum ReceiveError {
629    #[error("i/o error")]
630    Io(#[from] io::Error),
631    #[error("missing packet header")]
632    NoHeader,
633    #[error("release fds packet too small")]
634    BadReleaseFds,
635    #[error("unknown packet type {0:?}")]
636    UnknownPacketType(protocol::PacketType),
637    #[cfg(target_os = "linux")]
638    #[error("memfd file descriptor not sent for large event")]
639    MissingMemfd,
640    #[cfg(target_os = "linux")]
641    #[error("failed to map memfd")]
642    Memfd(#[source] io::Error),
643}
644
645/// Handles receive processing for the socket.
646async fn run_receive(
647    local_node: &LocalNode,
648    remote_id: &NodeId,
649    socket: &UnixSocket,
650    send: &mpsc::UnboundedSender<SenderCommand>,
651) -> Result<(), ReceiveError> {
652    let mut buf = vec![0; MAX_PACKET_SIZE];
653    let mut fds = Vec::new();
654    let mut receiver = unix_socket::ScmReceiver::new(protocol::MAX_FDS_PER_MESSAGE);
655    loop {
656        let len = socket.recv(&mut buf, &mut fds, &mut receiver).await?;
657        if len == 0 {
658            break;
659        }
660        if cfg!(target_os = "macos") && !fds.is_empty() {
661            // Tell the opposite endpoint to release the fds it sent.
662            let _ = send.unbounded_send(SenderCommand::Send {
663                packet: protocol::ReleaseFds {
664                    header: protocol::PacketHeader {
665                        packet_type: protocol::PacketType::RELEASE_FDS,
666                        ..FromZeros::new_zeroed()
667                    },
668                    count: fds.len() as u64,
669                }
670                .as_bytes()
671                .to_vec(),
672                fds: Vec::new(),
673            });
674        }
675
676        let buf = &buf[..len];
677        let header = protocol::PacketHeader::read_from_prefix(buf)
678            .map_err(|_| ReceiveError::NoHeader)?
679            .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
680        match header.packet_type {
681            protocol::PacketType::EVENT => {
682                local_node.event(remote_id, &buf[size_of_val(&header)..], &mut fds);
683                fds.clear();
684            }
685            protocol::PacketType::RELEASE_FDS => {
686                let release_fds = protocol::ReleaseFds::read_from_prefix(buf)
687                    .map_err(|_| ReceiveError::BadReleaseFds)?
688                    .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
689                let _ = send.unbounded_send(SenderCommand::ReleaseFds {
690                    count: release_fds.count as usize,
691                });
692            }
693            #[cfg(target_os = "linux")]
694            protocol::PacketType::LARGE_EVENT => {
695                if fds.is_empty() {
696                    return Err(ReceiveError::MissingMemfd);
697                }
698                let OsResource::Fd(fd) = fds.remove(0);
699                let memfd = memfd::SealedMemfd::new(fd.into()).map_err(ReceiveError::Memfd)?;
700                local_node.event(remote_id, &memfd, &mut fds);
701                fds.clear();
702            }
703            ty => {
704                return Err(ReceiveError::UnknownPacketType(ty));
705            }
706        }
707    }
708    Ok(())
709}
710
711#[derive(Debug, Error)]
712enum ProtocolError {
713    #[error("request to release too many fds")]
714    ReleasingTooManyFds,
715}
716
717/// Handles send processing for the socket.
718async fn run_send(
719    mut recv: mpsc::UnboundedReceiver<SenderCommand>,
720    socket: &UnixSocket,
721    retained_fds: &mut VecDeque<OsResource>,
722) -> io::Result<()> {
723    while let Some(command) = recv.next().await {
724        match command {
725            SenderCommand::Send { packet, fds } => {
726                match socket.send(&packet, &fds).await {
727                    Ok(_) => (),
728                    Err(err) => {
729                        tracing::error!(
730                            fd_count = fds.len(),
731                            packet_len = packet.len(),
732                            "failed to send packet"
733                        );
734                        return Err(err);
735                    }
736                }
737                if cfg!(target_os = "macos") {
738                    // MacOS has a bug where it prematurely closes Unix sockets
739                    // if a file descriptor to one is closed while it is also in
740                    // the process of being sent across another Unix socket.
741                    // Retain the fds until the opposite endpoint sends a reply
742                    // message.
743                    if !fds.is_empty() {
744                        retained_fds.extend(fds);
745                    }
746                }
747            }
748            SenderCommand::ReleaseFds { count } => {
749                if retained_fds.len() < count {
750                    return Err(io::Error::other(ProtocolError::ReleasingTooManyFds));
751                }
752                retained_fds.drain(..count);
753            }
754        }
755    }
756    Ok(())
757}
758
759/// An offer to take over as leader of the mesh. One trusted process must be the
760/// leader at all times or the mesh will fail.
761#[derive(Debug, Protobuf)]
762#[mesh(resource = "Resource")]
763pub struct LeadershipOffer {
764    send: mesh_channel::Sender<(NodeId, mesh_channel::Sender<Followers>)>,
765}
766
767impl UnixNode {
768    /// Creates a new Unix node mesh with this node as the leader.
769    pub fn new(driver: impl SpawnDriver) -> Self {
770        let (to_leader_send, to_leader_recv) = channel();
771        let (from_leader_send, from_leader_recv) = channel();
772        let this = Self::with_id(
773            Arc::new(driver),
774            NodeId::new(),
775            to_leader_send,
776            from_leader_recv,
777        );
778
779        // Start a leader task. At this point the leader has only one follower, itself.
780        let (resign_send, resign_recv) = channel();
781        let resign_send = Arc::new(resign_send);
782        let followers = Followers {
783            list: vec![(this.local_node.id(), to_leader_recv, from_leader_send)],
784        };
785        let task = SmallTask::new("run_leader", {
786            let local_node = this.local_node.clone();
787            let tasks = this.tasks.clone();
788            let driver = this.driver.clone();
789            async move { run_leader(driver.as_ref(), &local_node, resign_recv, followers, &tasks).await }
790        });
791        this.tasks.send(task);
792        *this.leader_resign_send.lock() = Some(resign_send);
793
794        this
795    }
796
797    /// Gets the node ID. This is mostly useful for diagnostics.
798    pub fn id(&self) -> NodeId {
799        self.local_node.id()
800    }
801
802    /// Creates a node with `id` using `to_leader` and `from_leader` to
803    /// communicate with the leader node.
804    fn with_id(
805        driver: Arc<dyn SpawnDriver>,
806        id: NodeId,
807        to_leader: mesh_channel::Sender<LeaderRequest>,
808        from_leader: mesh_channel::Receiver<FollowerRequest>,
809    ) -> Self {
810        let to_leader = Arc::new(to_leader);
811        let pending_connections: Arc<Mutex<HashMap<NodeId, RemoteNodeHandle>>> = Default::default();
812        let local_node = Arc::new(LocalNode::with_id(
813            id,
814            Box::new(Connector {
815                local_id: id,
816                conn_req_send: to_leader.clone(),
817                pending_connections: pending_connections.clone(),
818            }),
819        ));
820        let (task_send, mut task_recv) = channel::<SmallTask>();
821        let task_send = Arc::new(task_send);
822        let (drop_send, drop_recv) = oneshot();
823
824        // Start a thread to run IO tasks.
825        let io_task = driver.spawn("unix-mesh-io", async move {
826            let process = async {
827                let mut futs = FuturesUnordered::new();
828                loop {
829                    futures::select! { // merge semantics
830                        _ = futs.next() => {},
831                        task = task_recv.select_next_some() => {
832                            futs.push(async move {
833                                tracing::trace!(?id, name = task.name, "task start");
834                                task.future.await;
835                                tracing::trace!(?id, name = task.name, "task end");
836                            });
837                        },
838                        complete => break,
839                    };
840                }
841            };
842            future::select(pin!(process), drop_recv).await;
843        });
844
845        task_send.send(SmallTask::new("run_follower", {
846            let local_node = local_node.clone();
847            let tasks = task_send.clone();
848            let driver = driver.clone();
849            async move {
850                run_follower(
851                    driver.as_ref(),
852                    &local_node,
853                    from_leader,
854                    pending_connections,
855                    &tasks,
856                )
857                .await
858            }
859        }));
860
861        Self {
862            driver,
863            local_node,
864            tasks: task_send,
865            io_task,
866            to_leader,
867            leader_resign_send: Mutex::new(None),
868
869            _drop_send: drop_send,
870        }
871    }
872
873    /// Returns an offer to hand the leadership to another node in the mesh.
874    ///
875    /// The offer should be sent over a channel and passed to
876    /// `accept_leadership` in the receiving node.
877    pub fn offer_leadership(&self) -> LeadershipOffer {
878        let (send, mut recv) = channel();
879        if let Some(leader_send) = self.leader_resign_send.lock().clone() {
880            // Start a task to wait for the offer to be acknowledged, then send
881            // the offer details to the leader thread.
882            let task = SmallTask::new("offer_leadership", async move {
883                if let Ok(r) = recv.recv().await {
884                    leader_send.send(r);
885                }
886            });
887            self.tasks.send(task);
888        }
889        LeadershipOffer { send }
890    }
891
892    /// Accepts a leadership offer, making this node the current leader.
893    pub fn accept_leadership(&self, offer: LeadershipOffer) {
894        let (send, mut recv) = channel();
895        offer.send.send((self.local_node.id(), send));
896
897        let (resign_send, resign_recv) = channel();
898        let resign_send = Arc::new(resign_send);
899        let task = SmallTask::new("accept_and_run_leader", {
900            let local_node = self.local_node.clone();
901            let tasks = self.tasks.clone();
902            let driver = self.driver.clone();
903            async move {
904                if let Ok(followers) = recv.recv().await {
905                    drop(recv);
906                    run_leader(driver.as_ref(), &local_node, resign_recv, followers, &tasks).await
907                }
908            }
909        });
910        self.tasks.send(task);
911        *self.leader_resign_send.lock() = Some(resign_send);
912    }
913
914    /// Invites another process to join the mesh, with `port` bridged with the
915    /// original port.
916    #[instrument(skip_all, fields(local_id = ?self.local_node.id()))]
917    pub async fn invite(&self, port: Port) -> Result<Invitation, InviteError> {
918        let (invitation_send, mut invitation_recv) = channel();
919        self.to_leader
920            .send(LeaderRequest::Invite(port, invitation_send));
921        let invitation = invitation_recv.recv().await.map_err(|_| InviteError)?;
922        tracing::debug!(
923            invite_id = ?invitation.address.local_addr.node,
924            "received invitation",
925        );
926        Ok(invitation)
927    }
928
929    /// Extract a [`UnixMeshInviter`] handle. Cheap — clones an Arc.
930    ///
931    /// The inviter can be used from any task to create mesh invitations
932    /// without holding a reference to this node.
933    pub(crate) fn inviter(&self) -> UnixMeshInviter {
934        UnixMeshInviter {
935            to_leader: (*self.to_leader).clone(),
936        }
937    }
938
939    /// Joins an existing mesh via an invitation, briding `port` with the
940    /// initial port.
941    pub async fn join(
942        driver: impl SpawnDriver,
943        invitation: Invitation,
944        port: Port,
945    ) -> Result<Self, JoinError> {
946        Self::join_generic(Arc::new(driver), invitation, port).await
947    }
948
949    #[instrument(skip_all, fields(local_id = ?invitation.address.local_addr.node, remote_id = ?invitation.address.remote_addr.node))]
950    async fn join_generic(
951        driver: Arc<dyn SpawnDriver>,
952        invitation: Invitation,
953        port: Port,
954    ) -> Result<Self, JoinError> {
955        let (to_leader_send, to_leader_recv) = channel();
956        let (from_leader_send, from_leader_recv) = channel();
957        let this = Self::with_id(
958            driver,
959            invitation.address.local_addr.node,
960            to_leader_send,
961            from_leader_recv,
962        );
963
964        let handle = this
965            .local_node
966            .add_remote(invitation.address.remote_addr.node);
967        let init_recv = OneshotReceiver::<InitialMessage>::from(this.local_node.add_port(
968            invitation.address.local_addr.port,
969            invitation.address.remote_addr,
970        ));
971
972        start_connection(
973            &this.tasks,
974            &this.local_node,
975            invitation.address.remote_addr.node,
976            handle,
977            UnixSocket::new(this.driver.as_ref(), invitation.fd.into()),
978        );
979
980        let init_message = init_recv.await.map_err(JoinError)?;
981        to_leader_recv.bridge(init_message.leader_send);
982        from_leader_send.bridge(init_message.follower_recv);
983        port.bridge(init_message.user_port);
984
985        Ok(this)
986    }
987
988    /// Shuts down the node, waiting for any sent messages to be sent to their
989    /// destination.
990    ///
991    /// After this call, any active ports will no longer be able to receive
992    /// messages.
993    ///
994    /// It is essential to call this before exiting a mesh process; until this
995    /// returns, data loss could occur for other mesh nodes.
996    pub async fn shutdown(mut self) {
997        // Wait for any proxy ports to disassociate.
998        self.local_node.wait_for_ports(false).await;
999        // Drop all connections to the leader.
1000        drop(self.to_leader);
1001        self.local_node.drop_connector();
1002        // Terminate the leader task.
1003        self.leader_resign_send.get_mut().take();
1004        // Fail all nodes so that the send threads are dropped.
1005        self.local_node.fail_all_nodes();
1006        // Signal the IO task to tear down.
1007        drop(self.tasks);
1008        // Wait for the IO task.
1009        self.io_task.await;
1010    }
1011}
1012
1013/// An error returned by [`UnixNode::join`].
1014#[derive(Debug, Error)]
1015#[error("failed to accept invitation")]
1016pub struct JoinError(#[source] RecvError);
1017
1018/// The connector used when the mesh needs to connect to a previously-recognized
1019/// node. Sends a message to the leader node to get a new socket to communicate
1020/// over.
1021#[derive(Debug)]
1022struct Connector {
1023    local_id: NodeId,
1024    conn_req_send: Arc<mesh_channel::Sender<LeaderRequest>>,
1025    pending_connections: Arc<Mutex<HashMap<NodeId, RemoteNodeHandle>>>,
1026}
1027
1028impl Connect for Connector {
1029    fn connect(&self, node_id: NodeId, handle: RemoteNodeHandle) {
1030        tracing::trace!(local_id = ?self.local_id, remote_id = ?node_id, "connecting");
1031        let old_request = self.pending_connections.lock().insert(node_id, handle);
1032        if old_request.is_some() {
1033            panic!("duplicate connection request for {:?}", node_id);
1034        }
1035        self.conn_req_send.send(LeaderRequest::Connect(node_id))
1036    }
1037}
1038
1039/// Creates an AF_UNIX socket pair of the appropriate type.
1040fn new_socket_pair() -> Result<(Socket, Socket), io::Error> {
1041    let ty = if USE_SEQPACKET {
1042        socket2::Type::SEQPACKET
1043    } else {
1044        socket2::Type::STREAM
1045    };
1046    Socket::pair(socket2::Domain::UNIX, ty, None)
1047}
1048
1049/// An AF_UNIX SOCK_SEQPACKET connection.
1050struct UnixSocket {
1051    socket: Mutex<PolledSocket<Socket>>,
1052}
1053
1054impl UnixSocket {
1055    fn new(driver: &dyn SpawnDriver, fd: Socket) -> Self {
1056        let socket = PolledSocket::new(driver, fd).unwrap();
1057        UnixSocket {
1058            socket: Mutex::new(socket),
1059        }
1060    }
1061
1062    async fn send(&self, msg: &[u8], fds: &[OsResource]) -> io::Result<()> {
1063        if USE_SEQPACKET {
1064            self.send_raw(&mut [IoSlice::new(msg)], fds).await?;
1065        } else {
1066            let len = (msg.len() as u32).to_le_bytes();
1067            let mut iov = [IoSlice::new(&len), IoSlice::new(msg)];
1068            self.send_all_raw(&mut iov, fds).await?;
1069        }
1070        Ok(())
1071    }
1072
1073    async fn send_raw(
1074        &self,
1075        iov: &mut [IoSlice<'_>],
1076        fds: &[OsResource],
1077    ) -> Result<usize, io::Error> {
1078        // Bound the fd count to what the receiver can hold. Without this,
1079        // attaching more than `MAX_FDS_PER_MESSAGE` descriptors would
1080        // deterministically trip `MSG_CTRUNC` on the receiver and tear down the
1081        // connection; reject it locally with a clear error instead.
1082        if fds.len() > protocol::MAX_FDS_PER_MESSAGE {
1083            return Err(io::Error::new(
1084                ErrorKind::InvalidInput,
1085                format!(
1086                    "too many file descriptors ({}, max {})",
1087                    fds.len(),
1088                    protocol::MAX_FDS_PER_MESSAGE
1089                ),
1090            ));
1091        }
1092        let n = poll_fn(|cx| {
1093            self.socket
1094                .lock()
1095                .poll_io(cx, InterestSlot::Write, PollEvents::OUT, |socket| {
1096                    unix_socket::send_with_fds(
1097                        socket.get().as_fd(),
1098                        iov,
1099                        fds.iter().map(|OsResource::Fd(fd)| fd.as_fd()),
1100                    )
1101                })
1102        })
1103        .await?;
1104        Ok(n)
1105    }
1106
1107    async fn send_all_raw(
1108        &self,
1109        mut iov: &mut [IoSlice<'_>],
1110        mut fds: &[OsResource],
1111    ) -> Result<(), io::Error> {
1112        while !iov.is_empty() || !fds.is_empty() {
1113            let n = self.send_raw(iov, fds).await?;
1114            IoSlice::advance_slices(&mut iov, n);
1115            fds = &[];
1116        }
1117        Ok(())
1118    }
1119
1120    async fn recv(
1121        &self,
1122        buf: &mut [u8],
1123        fds: &mut Vec<OsResource>,
1124        receiver: &mut unix_socket::ScmReceiver,
1125    ) -> io::Result<usize> {
1126        if USE_SEQPACKET {
1127            self.recv_raw(buf, fds, receiver).await
1128        } else {
1129            let mut len = [0; 4];
1130            if !self.recv_all_raw(&mut len, fds, receiver).await? {
1131                return Ok(0);
1132            }
1133            let len = u32::from_le_bytes(len) as usize;
1134            let buf = buf
1135                .get_mut(..len)
1136                .ok_or_else(|| io::Error::from_raw_os_error(libc::EMSGSIZE))?;
1137            if !self.recv_all_raw(buf, fds, receiver).await? {
1138                return Err(ErrorKind::UnexpectedEof.into());
1139            }
1140            Ok(len)
1141        }
1142    }
1143
1144    async fn recv_all_raw(
1145        &self,
1146        buf: &mut [u8],
1147        fds: &mut Vec<OsResource>,
1148        receiver: &mut unix_socket::ScmReceiver,
1149    ) -> Result<bool, io::Error> {
1150        let mut read = 0;
1151        while read < buf.len() {
1152            let n = self.recv_raw(&mut buf[read..], fds, receiver).await?;
1153            if n == 0 {
1154                if read != 0 {
1155                    return Err(ErrorKind::UnexpectedEof.into());
1156                } else {
1157                    return Ok(false);
1158                }
1159            }
1160            read += n;
1161        }
1162        Ok(true)
1163    }
1164
1165    async fn recv_raw(
1166        &self,
1167        buf: &mut [u8],
1168        fds: &mut Vec<OsResource>,
1169        receiver: &mut unix_socket::ScmReceiver,
1170    ) -> Result<usize, io::Error> {
1171        let n = poll_fn(|cx| {
1172            self.socket
1173                .lock()
1174                .poll_io(cx, InterestSlot::Read, PollEvents::IN, |socket| {
1175                    receiver.recv(socket.get().as_fd(), buf)
1176                })
1177        })
1178        .await?;
1179        fds.extend(receiver.drain().map(OsResource::Fd));
1180        Ok(n)
1181    }
1182
1183    async fn close_write(&self) -> io::Result<()> {
1184        self.socket.lock().get().shutdown(std::net::Shutdown::Write)
1185    }
1186}
1187
1188#[cfg(test)]
1189mod tests {
1190    use crate::unix::UnixNode;
1191    use mesh_channel::RecvError;
1192    use mesh_channel::channel;
1193    use pal_async::DefaultDriver;
1194    use pal_async::async_test;
1195    use test_with_tracing::test;
1196
1197    #[async_test]
1198    async fn test_basic(driver: DefaultDriver) {
1199        let leader = UnixNode::new(driver.clone());
1200        let (send, recv) = channel::<u32>();
1201        let invitation = leader.invite(recv.into()).await.unwrap();
1202        let (send2, mut recv2) = channel::<u32>();
1203        let follower = UnixNode::join(driver, invitation, send2.into())
1204            .await
1205            .unwrap();
1206        send.send(5);
1207        assert_eq!(recv2.recv().await.unwrap(), 5);
1208        drop(send);
1209        drop(recv2);
1210        follower.shutdown().await;
1211        leader.shutdown().await;
1212    }
1213
1214    #[cfg(target_os = "linux")]
1215    #[async_test]
1216    async fn test_huge_message(driver: DefaultDriver) {
1217        let leader = UnixNode::new(driver.clone());
1218        let (send, recv) = channel::<Vec<u8>>();
1219        let invitation = leader.invite(recv.into()).await.unwrap();
1220        let (send2, mut recv2) = channel::<Vec<u8>>();
1221        let follower = UnixNode::join(driver, invitation, send2.into())
1222            .await
1223            .unwrap();
1224
1225        let v = vec![0xcc; 16 << 20];
1226        send.send(v.clone());
1227        let v2 = recv2.recv().await.unwrap();
1228        assert_eq!(v, v2);
1229        follower.shutdown().await;
1230        leader.shutdown().await;
1231    }
1232
1233    #[cfg(target_os = "linux")]
1234    #[async_test]
1235    async fn test_message_sizes(driver: DefaultDriver) {
1236        let (p1, p2) = mesh_node::local_node::Port::new_pair();
1237        let (p3, p4) = mesh_node::local_node::Port::new_pair();
1238        let node1 = UnixNode::new(driver.clone());
1239        let invitation = node1.invite(p2).await.unwrap();
1240        let _node2 = UnixNode::join(driver.clone(), invitation, p3)
1241            .await
1242            .unwrap();
1243
1244        crate::test_common::test_message_sizes(p1, p4, 0..=super::MAX_SMALL_EVENT_SIZE + 0x1000)
1245            .await;
1246    }
1247
1248    #[async_test]
1249    async fn test_dropped_shutdown(driver: DefaultDriver) {
1250        let leader = UnixNode::new(driver.clone());
1251        {
1252            let (_send, recv) = channel::<u32>();
1253            let invitation = leader.invite(recv.into()).await.unwrap();
1254            let (send2, _recv2) = channel::<u32>();
1255            let _follower = UnixNode::join(driver, invitation, send2.into())
1256                .await
1257                .unwrap();
1258        }
1259        leader.shutdown().await;
1260    }
1261
1262    #[async_test]
1263    async fn test_send_shutdown(driver: DefaultDriver) {
1264        let leader = UnixNode::new(driver.clone());
1265        let (send, mut recv) = channel::<u32>();
1266        let invitation = leader.invite(send.into()).await.unwrap();
1267        let (send2, recv2) = channel::<u32>();
1268        let follower = UnixNode::join(driver, invitation, recv2.into())
1269            .await
1270            .unwrap();
1271        send2.send(5);
1272        drop(send2);
1273        follower.shutdown().await;
1274        assert_eq!(recv.recv().await.unwrap(), 5);
1275    }
1276
1277    #[async_test]
1278    async fn test_failed_invitation(driver: DefaultDriver) {
1279        let leader = UnixNode::new(driver);
1280        let (send, mut recv) = channel::<()>();
1281        leader.invite(send.into()).await.unwrap();
1282        assert!(matches!(
1283            recv.recv().await.unwrap_err(),
1284            RecvError::Error(_)
1285        ));
1286        drop(recv);
1287        leader.shutdown().await;
1288    }
1289
1290    #[async_test]
1291    async fn test_three(driver: DefaultDriver) {
1292        let (p1, p2) = channel::<u32>();
1293        let (p3, mut p4) = channel::<u32>();
1294        let (p5, p6) = channel::<u32>();
1295        let (p7, p8) = channel::<u32>();
1296
1297        let node1 = UnixNode::new(driver.clone());
1298
1299        let invitation = node1.invite(p2.into()).await.unwrap();
1300        let node2 = UnixNode::join(driver.clone(), invitation, p3.into())
1301            .await
1302            .unwrap();
1303
1304        let invitation = node1.invite(p5.into()).await.unwrap();
1305        let node3 = UnixNode::join(driver, invitation, p8.into()).await.unwrap();
1306
1307        p1.bridge(p6);
1308
1309        p7.send(5);
1310
1311        assert_eq!(p4.recv().await.unwrap(), 5);
1312        drop(p4);
1313        drop(p7);
1314        futures::join!(node2.shutdown(), node3.shutdown());
1315        node1.shutdown().await;
1316    }
1317
1318    #[async_test]
1319    async fn test_handoff_leader(driver: DefaultDriver) {
1320        let (p1, p2) = channel::<u32>();
1321        let (p3, p4) = channel::<u32>();
1322        let (p5, p6) = channel::<u32>();
1323        let (p7, p8) = channel::<u32>();
1324        let (p9, p10) = channel();
1325        let (p11, mut p12) = channel();
1326
1327        let node1 = UnixNode::new(driver.clone());
1328
1329        let invitation = node1.invite(p2.into()).await.unwrap();
1330        let node2 = UnixNode::join(driver.clone(), invitation, p3.into())
1331            .await
1332            .unwrap();
1333
1334        let invitation = node1.invite(p5.into()).await.unwrap();
1335        let node3 = UnixNode::join(driver.clone(), invitation, p8.into())
1336            .await
1337            .unwrap();
1338
1339        let invitation = node1.invite(p10.into()).await.unwrap();
1340        let node4 = UnixNode::join(driver, invitation, p11.into())
1341            .await
1342            .unwrap();
1343
1344        p9.send(node1.offer_leadership());
1345        node4.accept_leadership(p12.recv().await.unwrap());
1346        drop(p9);
1347        drop(p12);
1348        p1.bridge(p6);
1349
1350        std::thread::sleep(std::time::Duration::from_millis(200));
1351
1352        node1.shutdown().await;
1353        drop(p4);
1354        drop(p7);
1355        node2.shutdown().await;
1356        node3.shutdown().await;
1357
1358        std::thread::sleep(std::time::Duration::from_millis(200));
1359
1360        node4.shutdown().await;
1361    }
1362}