A super minimal wrapper around unix sockets for IPC on top of tokio.

Overview

tokio-unix-ipc

This crate implements a minimal abstraction over UNIX domain sockets for the purpose of IPC on top of tokio. It lets you send both file handles and rust objects between processes. This is a replacement for my earlier unix-ipc crate.

How it works

This uses serde to serialize data over unix sockets via bincode. Thanks to the Handle abstraction you can also send any object across that is convertable into a unix file handle.

The way this works under the hood is that during serialization and deserialization encountered file descriptors are tracked. They are then sent over the unix socket separately. This lets unassociated processes share file handles.

If you only want the unix socket abstraction you can disable all default features and use the raw channels.

Feature Flags

All features are enabled by default but a lot can be turned off to cut down on dependencies. With all default features enabled only the raw types are available.

  • serde: enables serialization and deserialization.
  • bootstrap: adds the Bootstrapper type.

License: MIT/Apache-2.0

Comments
  • Add a `channel_from_std` API

    Add a `channel_from_std` API

    Add a channel_from_std API

    This is more obvious to use from e.g. systemd socket activation, where we're passed a socket allocated outside the current process.


    typed_channel: Make channel asymmetric, add symmetric_channel

    To better handle the general case of asymmetric protocols.

    I originally tried to just change channel() but it caused a lot of pain in type inference in the test suite. Change most of the tests to use a symmetric_channel() as a convenient shorthand, with one new test for an asymmetric channel().


    opened by cgwalters 5
  • Try to make Bootstrapper thread safe

    Try to make Bootstrapper thread safe

    https://github.com/mitsuhiko/tokio-unix-ipc/issues/2

    Attempt to make Bootstrapper thread safe.

    1. Bootstrapper.sender change from RefCell to tokio::sync::Mutex.
    2. Add an example (async_trait_example.rs).

    I am not familiar with unix domain socket programming, maybe IPC_FDS and PANIC_INFO should change to tokio::sync::Mutex?

    opened by Christina2333 3
  • raw: Add methods to send and receive msgs with Unix credentials

    raw: Add methods to send and receive msgs with Unix credentials

    I have several projects that might make use of this crate, and one of them today uses SO_PEERCRED to validate the sender's identity.

    Add basic support for this.

    I chose to make a small new struct for credentials instead of exposing nix in our public types (perhaps we want to use rustix in the future, etc.)

    Closes: https://github.com/mitsuhiko/tokio-unix-ipc/issues/4

    opened by cgwalters 3
  • raw: Make `from_std` public

    raw: Make `from_std` public

    This method is just a wrapper around already public APIs, and it's convenient to use directly; in my case I have a socket already passed via systemd socket activation.

    opened by cgwalters 2
  • Cannot use Bootstrapper in a tokio task.

    Cannot use Bootstrapper in a tokio task.

    If I try to do the code snippet below, to have a background task wait for a message over IPC I get a compiler error:

    `Cell<isize>` cannot be shared between threads safely
    

    Is there any reason the sender field of Bootstrapper can't be changed from a RefCell to an Arc which would make this possible? Or is there a different way to go about what I am trying to achieve?

    tokio::spawn(async move {
            let bootstrapper = {
                let bootstrapper = tokio_unix_ipc::Bootstrapper::bind("/tmp/test-uds").unwrap();
                bootstrapper.send(Message::Sender(tx)).await.unwrap();
                bootstrapper
            };
    
            tokio::select! {
                res = rx.recv() => {
                    if let Ok(msg) = res {
                        match msg {
                            _ => {}
                        }
                    }
                }
            };
    });
    
    opened by conorbros 2
  • How to pass file descriptors between processes with it ?

    How to pass file descriptors between processes with it ?

    I tried to send the file descriptor using Bootstrapper.send() method but it doesn't work. Bootstrapper.send() method seems to transmit the file descriptor as an ordinary integer, without conversion by the operating system kernel. Similar to the functionality of this crate: https://github.com/polachok/passfd.

    There is another question I wonder: Why is the Bootstrapper#sender wrapped in RefCell instead of Arc, so that Bootstrapper cannot be used in asynchronous traits.

    Looking forward to your reply!

    opened by Christina2333 1
  • support for `SO_PEERCRED`

    support for `SO_PEERCRED`

    In one project I'm using SO_PEERCRED: https://github.com/coreos/bootupd/blob/main/src/ipc.rs - looking at using this crate instead. What do you think about exposing that via an API?

    The usual pattern here is to pass credentials on the first message. In the above code I represented this as a state machine encoded via structs; e.g. perhaps we'd add RawAuthenticatingSender and RawUnauthenticatedReceiver, where e.g.

    impl RawUnauthenticatedReceiver {
       pub async fn recv(self) -> Result<(RawReceiver, Credentials, Vec<u8>, Option<Vec<RawFd>>)>
    

    Note this consumes the unauthenticated type and returns credentials. Would need to debate exposing e.g. https://docs.rs/nix/latest/nix/sys/socket/struct.UnixCredentials.html or (probably better for semver) a new struct here.

    Alternatively we could add recv_with_creds() and send_with_creds() to the raw types.

    opened by cgwalters 1
  • Cannot use Bootstrapper.send in a async trait

    Cannot use Bootstrapper.send in a async trait

    I want to wrap Bootstrapper in a async trait, which is consistent of RefCell<Option<RawSender>> and is not thread safe.

    Here is a complete demo, TaskService is an asynchronous trait of Task abstraction. IpcSender is wrap of Bootstrapper, and IpcReceiver is wrap of Receiver<Task>. Both of them implement TaskService to send or receive ipc request.

    use std::{env, process};
    use tokio_unix_ipc::{Bootstrapper, channel, Receiver, Sender};
    use serde::Serialize;
    use serde::Deserialize;
    
    const ENV_VAR: &str = "PROC_CONNECT_TO";
    
    #[derive(Serialize, Deserialize)]
    pub enum Task {
        Sum(Vec<i64>, Sender<i64>),
        Shutdown
    }
    
    #[async_trait::async_trait]
    pub trait TaskService {
        async fn sum(&self, vec: Vec<i64>) -> i64;
        async fn shutdown(&self);
    }
    
    #[tokio::main]
    async fn main() {
        if let Ok(path) = env::var(ENV_VAR) {
            let server = IpcReceiver::new(path).await;
            server.start().await;
        } else {
            let client = IpcSender::new();
            let mut child = process::Command::new(env::current_exe().unwrap())
                .env(ENV_VAR, client.bootstrap.path())
                .spawn()
                .unwrap();
    
            let sum = client.sum(vec![1, 2, 3]).await;
            println!("sum = {}", sum);
    
            child.kill().ok();
            child.wait().ok();
        }
    }
    
    
    /// ipc_receiver
    pub struct IpcReceiver {
        pub recv: Receiver<Task>,
    }
    impl IpcReceiver {
        pub async fn new(path: String) -> Self {
            let receiver = Receiver::<Task>::connect(path).await.unwrap();
            IpcReceiver {
                recv: receiver,
            }
        }
        pub async fn start(&self) {
            loop {
                let task = self.recv.recv().await.unwrap();
                match task {
                    Task::Sum(vec, tx) => {
                        let sum = self.sum(vec).await;
                        tx.send(sum).await.unwrap();
                    },
                    Task::Shutdown => {
                        self.shutdown().await;
                        break
                    }
                }
            }
        }
    }
    #[async_trait::async_trait]
    impl TaskService for IpcReceiver {
        async fn sum(&self, vec: Vec<i64>) -> i64 {
            vec.into_iter().sum::<i64>()
        }
    
        async fn shutdown(&self) {
        }
    }
    
    /// ipc_sender
    pub struct IpcSender {
        pub bootstrap: Bootstrapper,
    }
    impl IpcSender {
        pub fn new() -> Self {
            let bootstrapper = Bootstrapper::new().unwrap();
            IpcSender {
                bootstrap: bootstrapper
            }
        }
    }
    #[async_trait::async_trait]
    impl TaskService for IpcSender {
        async fn sum(&self, vec: Vec<i64>) -> i64 {
            let (tx, rx) = channel().unwrap();
            self.bootstrap.send(Task::Sum(vec, tx));
            rx.recv().await.unwrap()
        }
    
        async fn shutdown(&self) {
            self.bootstrap.send(Task::Shutdown).await.unwrap()
        }
    }
    

    I wonder if Bootstrapper#sender can change to Mutex. Maybe I can modify it later.

    opened by Christina2333 0
  • Fix readme

    Fix readme

    https://docs.rs/unix-ipc/latest/unix-ipc/struct.Handle.html is wrong, should be https://docs.rs/unix-ipc/0.2.0/unix_ipc/struct.Handle.html

    see also https://github.com/mitsuhiko/unix-ipc/pull/2

    opened by barrowsys 0
  • from_std() does not set socket to non-blocking mode

    from_std() does not set socket to non-blocking mode

    Tokio's UnixStream::from_std does not set the stream to non-blocking, it's up to the caller to do so: https://docs.rs/tokio/latest/tokio/net/struct.UnixStream.html#method.from_std

    They seem unwilling to fix this particular footgun because of backwards compatibility, but tokio_unix_ipc at least could set the stream to non-blocking so users don't have to remember to do so.

    enhancement 
    opened by itamarst 0
  • protocol stability

    protocol stability

    The protocol isn't documented today as far as I can tell. But I think this crate is implicitly committing to its stability - because any time one is doing IPC, the possibility of version drift between the binaries (and hence versions of this crate) comes into play. I'm sure the protocol wouldn't change across Rust semvers of this crate - but do you see it changing ever?

    In one use case I have that's similar to this crate, I have the client send the (device, inode) pair of /proc/self/exe over to the server, and the server verifies they match. (This also of course covers the higher-level protocol that applications would use on top of this crate; a related but distinct issue)

    One reason I ask this is I have a use case for doing IPC between Rust and Go, and I'm today doing JSON over SOCK_SEQPACKET but...while the great thing about SEQPACKET is the kernel takes care of the framing, it has less support in the userspace ecosystem, and a simple "[header with length][payload]" protocol has well-understood tooling because it's how a vast array of TCP protocols work.

    TL;DR: Would you say we could document the "raw" protocol used here and commit to its stability approximately ~forever?

    opened by cgwalters 10
Owner
Armin Ronacher
Software developer and Open Source nut. Creator of the Flask framework. Engineering at @getsentry. Other things of interest: @pallets and @rust-lang
Armin Ronacher
Super simple tokio chat server for educational purposes.

achat A collection of simple modules which showcase simple use of tasks, channels, and other tokio primitives to implement simple networking applicati

Rafael Bachmann 2 Dec 29, 2022
Modular IPC-based desktop launcher service

Pop Launcher Modular IPC-based desktop launcher service, written in Rust. Desktop launchers may interface with this service via spawning the pop-launc

Pop!_OS 125 Dec 23, 2022
An experimental IPC interface definition language for Hubris.

Idol: interface definitions for Hubris This is an experimental interface definition language for defining IPC interfaces between tasks in a Hubris app

Oxide Computer Company 8 Oct 19, 2022
Rust-native IPC broker

elbus - Rust-native IPC broker Note: the project is under development and in alpha stage https://elbus.bma.ai/ What is elbus elbus is a rust-native IP

Altertech 66 Dec 31, 2022
Super Fast Sub-domain Takeover Detection!

NtHiM - Super Fast Sub-domain Takeover Detection Installation Method 1: Using Pre-compiled Binaries The pre-compiled binaries for different systems ar

Binit Ghimire 326 Dec 31, 2022
A super naive, (possibly unreliable), VPN implementation.

Poor Man's VPN A super naive, (possibly unreliable), VPN implementation. Try on Docker Containers You can test the VPN on docker containers. up.sh scr

algon 2 Sep 7, 2022
Unix dgram, seqpacket, etc binding for Node.js.

node-unix-socket node-unix-socket allows you to use some nonblocking unix sockets that are currently not supported by Node.js native modules, includin

Bytedance Inc. 40 Jun 28, 2023
Rust Tokio 异步多客户端网络框架 高并发 插件化

Rust实现的异步多客户端网络框架,基于tokio和mlua,可自定义通讯协议 插件化采用lua。应用场景im,game server,bot等.

OPQBOT 22 Jul 19, 2022
A proxy implement with http / socks5 in-bound and vmess out-bound, written in Rust and tokio.rs

tokio-vmess an Asynchronous proxy implement with http / socks5 in-bound and vmess out-bound, written in Rust and tokio Run example first, Fill out the

irumeria 7 Oct 3, 2022
Stream API for tokio-udp.

UDPflow Stream API for tokio-udp. TCP-like UDP stream use tokio::net::UdpSocket; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use udpflow::{UdpListen

zephyr 5 Dec 2, 2022
Asynchronous Linux SocketCAN - Broadcast Manager support (BCM) with tokio

tokio-socketcan-bcm The Broadcast Manager protocol provides a command based configuration interface to filter and send (e.g. cyclic) CAN messages in k

Marcel 4 Nov 8, 2022
ZeroNS: a name service centered around the ZeroTier Central API

ZeroNS: a name service centered around the ZeroTier Central API ZeroNS provides names that are a part of ZeroTier Central's configured networks; once

ZeroTier, Inc. 327 Dec 20, 2022
The goal of this challenge is to create an isometric, decorated scene in which the character can move around the objects in the room.

The goal of this challenge is to create an isometric, decorated scene in which the character can move around the objects in the room.

Ivan Reshetnikov 0 Feb 4, 2022
A minimal ngrok liked reverse proxy implemented in Rust.

rok A minimal ngrok implementation in Rust, for educational purpose. This work is largely based on rathole, especially the very first commit. Other ho

Kai 3 Jun 21, 2022
A minimal, allocation-free Prometheus/OpenMetrics metrics implementation for `no-std` and embedded Rust.

tinymetrics a minimal, allocation-free Prometheus/OpenMetrics metrics implementation for no-std and embedded projects. why should you use it? you may

Eliza Weisman 282 Apr 16, 2023
Minimal self-contained crate to accept file descriptors from systemd

sd-listen-fds Exposes file descriptors passed in by systemd, the Linux init daemon, without dependencies, foreign or otherwise. Enables easy implement

Benjamin Saunders 3 Aug 14, 2023
Minimal DNS server built in Rust with rule system and logging.

MinDNS MinDNS is a minimal DNS server written in Rust. It is intended to be used as a firewall, black-hole or proxy DNS server. ⚡ Features Fully async

Sammwy 142 Oct 23, 2023
Rust wrapper for Eclipse iceoryx™ - true zero-copy inter-process-communication

iceoryx-rs Experimental rust wrapper for the iceoryx IPC middleware. clone and build The iceoryx repo is include as git submodule, therefore keep in m

null 43 Jan 4, 2023
A wrapper for the Google Cloud DNS API

cloud-dns is a crate providing a client to interact with Google Cloud DNS v1

Embark 5 May 24, 2022