1//
2// fd_passing_stream_server.cpp
3// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4//
5// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6// Copyright (c) 2021 Heiko Hund (heiko at openvpn dot net)
7//
8// Distributed under the Boost Software License, Version 1.0. (See accompanying
9// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
10//
11
12// Demonstrates how to pass file descriptors between processes with Asio.
13// The client sends a file name to the server. The server opens the file and
14// passes the associated file descriptor back to the client.
15
16#include <array>
17#include <cstdio>
18#include <cassert>
19#include <iostream>
20#include <memory>
21#include <boost/asio.hpp>
22
23#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
24
25using boost::asio::local::stream_protocol;
26
27class session
28 : public std::enable_shared_from_this<session>
29{
30public:
31 session(stream_protocol::socket sock)
32 : socket_(std::move(sock))
33 {
34 }
35
36 void start()
37 {
38 do_read();
39 }
40
41private:
42 void do_read()
43 {
44 auto self(shared_from_this());
45 socket_.async_read_some(buffers: boost::asio::buffer(data&: data_),
46 token: [this, self](boost::system::error_code ec, std::size_t length)
47 {
48 if (ec)
49 return;
50
51 assert(length < data_.size());
52 data_[length] = 0;
53 do_write(filename: data_.data());
54 });
55 }
56
57 void do_write(const char* filename)
58 {
59 auto self(shared_from_this());
60 socket_.async_wait(w: stream_protocol::socket::wait_write,
61 token: [this, self, filename](boost::system::error_code ec)
62 {
63 if (ec)
64 return;
65
66 FILE* f(::fopen(filename: filename, modes: "w+"));
67 if (!f)
68 return;
69
70 struct msghdr msg = {};
71 char buf[] = { 0 };
72 struct iovec iov = { .iov_base: buf, .iov_len: sizeof(buf) };
73 msg.msg_iov = &iov;
74 msg.msg_iovlen = 1;
75
76 union
77 {
78 struct cmsghdr align;
79 char buf[CMSG_SPACE(sizeof(int))];
80 } cmsgu;
81 msg.msg_control = cmsgu.buf;
82 msg.msg_controllen = sizeof(cmsgu.buf);
83
84 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
85 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
86 cmsg->cmsg_level = SOL_SOCKET;
87 cmsg->cmsg_type = SCM_RIGHTS;
88 int fd = ::fileno(stream: f);
89 std::memcpy(CMSG_DATA(cmsg), src: &fd, n: sizeof(int));
90
91 ssize_t s(::sendmsg(fd: socket_.native_handle(), message: &msg, flags: 0));
92 ::fclose(stream: f);
93 if (s != -1)
94 do_read();
95 });
96 }
97
98 // The socket used to communicate with the client.
99 stream_protocol::socket socket_;
100
101 // Buffer used to store data received from the client.
102 std::array<char, 1024> data_;
103};
104
105class server
106{
107public:
108 server(boost::asio::io_context& io_context, const std::string& file)
109 : acceptor_(io_context, stream_protocol::endpoint(file))
110 {
111 do_accept();
112 }
113
114private:
115 void do_accept()
116 {
117 acceptor_.async_accept(
118 token: [this](boost::system::error_code ec, stream_protocol::socket socket)
119 {
120 if (!ec)
121 {
122 std::make_shared<session>(args: std::move(socket))->start();
123 }
124
125 do_accept();
126 });
127 }
128
129 stream_protocol::acceptor acceptor_;
130};
131
132int main(int argc, char* argv[])
133{
134 try
135 {
136 if (argc != 2)
137 {
138 std::cerr << "Usage: fd_passing_stream_server <socketfile>\n";
139 std::cerr << "*** WARNING: existing file is removed ***\n";
140 return 1;
141 }
142
143 boost::asio::io_context io_context;
144
145 std::remove(filename: argv[1]);
146 server s(io_context, argv[1]);
147
148 io_context.run();
149 }
150 catch (std::exception& e)
151 {
152 std::cerr << "Exception: " << e.what() << "\n";
153 }
154
155 return 0;
156}
157
158#else // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
159# error Local sockets not available on this platform.
160#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
161

source code of boost/libs/asio/example/cpp11/local/fd_passing_stream_server.cpp