xdrpp
RFC4506 XDR compiler and message library
server.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 
3 //! \file server.h Classes for implementing RPC servers. We use the
4 //! following terminology: The term \e service denotes a class that
5 //! responds to RPCs for a particular program/version combination. A
6 //! \e server is a collection of services. A \e listener attaches
7 //! incoming connections from clients to a particular server. And a
8 //! \e session is state (if needed) associated with a particular
9 //! client connection.
10 
11 #ifndef _XDRPP_SERVER_H_HEADER_INCLUDED_
12 #define _XDRPP_SERVER_H_HEADER_INCLUDED_ 1
13 
14 #include <iostream>
15 #include <xdrpp/marshal.h>
16 #include <xdrpp/printer.h>
17 #include <xdrpp/msgsock.h>
18 #include <xdrpp/rpcbind.h>
19 #include <xdrpp/rpc_msg.hh>
20 #include <map>
21 
22 namespace xdr {
23 
24 extern bool xdr_trace_server;
25 
26 //! Structure that gets marshalled as an RPC success header.
28  uint32_t xid;
29  explicit constexpr rpc_success_hdr(uint32_t x) : xid(x) {}
30 };
32  static constexpr bool valid = true;
33  static constexpr bool is_class = true;
34  static constexpr bool is_struct = true;
35  static constexpr bool has_fixed_size = true;
36  static constexpr std::size_t fixed_size = 24;
37  static constexpr std::size_t serial_size(const rpc_success_hdr &) {
38  return fixed_size;
39  }
40  template<typename Archive> static void save(Archive &a,
41  const rpc_success_hdr &t) {
42  archive(a, t.xid, "xid");
43  archive(a, REPLY, "mtype");
44  archive(a, MSG_ACCEPTED, "stat");
45  archive(a, AUTH_NONE, "flavor");
46  archive(a, uint32_t(0), "body");
47  archive(a, SUCCESS, "stat");
48  }
49 };
50 
51 // The following produce various pre-formatted error responses.
52 msg_ptr rpc_accepted_error_msg(uint32_t xid, accept_stat stat);
53 msg_ptr rpc_prog_mismatch_msg(uint32_t xid, uint32_t low, uint32_t high);
54 msg_ptr rpc_auth_error_msg(uint32_t xid, auth_stat stat);
55 msg_ptr rpc_rpc_mismatch_msg(uint32_t xid);
56 
57 
58 //! A pointer, but that gets marshalled as the underlying object and
59 //! can convert to the underlying type. If \c p is a \c
60 //! transparent_ptr<T>, then \c std::move(p) can be passed as a \c
61 //! std::unique_ptr<T>, a \c T, or a <tt>const T&</tt>. This is what
62 //! allows some flexibility in the signatures of server object methods.
63 template<typename T> struct transparent_ptr : std::unique_ptr<T> {
64  using std::unique_ptr<T>::unique_ptr;
65  transparent_ptr() : std::unique_ptr<T>(new T{}) {}
66  operator T &() const { return *this->get(); }
67  operator T &&() { return std::move(*this->get()); }
68 };
69 
70 namespace detail {
71 template<typename T, bool fs = xdr_traits<T>::has_fixed_size>
72 struct transparent_ptr_base : xdr_traits_base {
73  static constexpr bool has_fixed_size = false;
74 };
75 template<typename T> struct transparent_ptr_base<T, true> : xdr_traits_base {
76  static constexpr bool has_fixed_size = true;
77  static constexpr std::size_t fixed_size = xdr_traits<T>::fixed_size;
78 };
79 }
80 
81 template<typename T> struct xdr_traits<transparent_ptr<T>>
82  : detail::transparent_ptr_base<T> {
83  using t_traits = xdr_traits<T>;
84  using ptr_type = std::unique_ptr<T>;
85 
86  static constexpr bool is_class = true;
87 
88  template<typename Archive> static void save(Archive &a, const ptr_type &p) {
89  archive(a, *p);
90  }
91  template<typename Archive> static void load(Archive &a, ptr_type &p) {
92  archive(a, *p);
93  }
94  static size_t serial_size(const ptr_type &p) {
95  return t_traits::serial_size(*p);
96  }
97 };
98 
99 // This is what makes the pointer transparent during marshaling.
100 template<typename Archive, typename T> inline void
101 archive(Archive &ar, const transparent_ptr<T> &t, const char *name = nullptr)
102 {
103  archive(ar, *t, name);
104 }
105 
106 namespace detail {
107 template<typename T> struct wrap_transparent_ptr_helper;
108 
109 template<typename...T>
110 struct wrap_transparent_ptr_helper<std::tuple<T...>> {
111  using type = std::tuple<transparent_ptr<T>...>;
112 };
113 }
114 
115 //! Wrap xdr::transparent_ptr around each type in a tuple to generate
116 //! a new tuple type.
117 template<typename T> using wrap_transparent_ptr =
118  typename detail::wrap_transparent_ptr_helper<T>::type;
119 
120 
121 namespace detail {
122 template<typename P, typename C, typename T, typename I = all_indices_of<T>>
123 struct dispatch_session_helper;
124 
125 template<typename P, typename C, typename T, std::size_t...I>
126 struct dispatch_session_helper<P, C, T, indices<I...>> {
127  template<typename S, typename...Rest> static auto
128  dispatch(C &&c, S *s, T &&t, Rest &&...rest) ->
129  typename std::enable_if<!std::is_same<S, void>::value,
130  decltype(P::dispatch(std::forward<C>(c), s,
131  std::get<I>(std::forward<T>(t))...,
132  std::forward<Rest>(rest)...))>::type
133  {
134  return P::dispatch(std::forward<C>(c), s,
135  std::get<I>(std::forward<T>(t))...,
136  std::forward<Rest>(rest)...);
137  }
138 
139  // If the previous one fails SFINAE, try omitting the session
140  // argument for methods that don't need it.
141  template<typename...Rest> static auto
142  dispatch(C &&c, void *, T &&t, Rest &&...rest) ->
143  decltype(P::dispatch(std::forward<C>(c), std::get<I>(std::forward<T>(t))...,
144  std::forward<Rest>(rest)...))
145  {
146  return P::dispatch(std::forward<C>(c), std::get<I>(std::forward<T>(t))...,
147  std::forward<Rest>(rest)...);
148  }
149 };
150 }
151 
152 //! Call \c P::dispatch with a session pointer (unless the session
153 //! type \c S is void, in which case the argument is omitted) and with
154 //! a tuple that should be unpacked into multiple arguments. For
155 //! example,
156 //! \code
157 //! dispatch_with_session<P>(c, (void *) 0, make_tuple(1, //! 2), 3, 4);
158 //! \endcode
159 //! is equivalent to
160 //! \code
161 //! P::dispatch(c, 1, 2, 3, 4);
162 //! \endcode
163 template<typename P, typename C, typename S, typename T,
164  typename...Rest> inline auto
165 dispatch_with_session(C &&c, S *s, T &&t, Rest &&...rest) ->
166  decltype(detail::dispatch_session_helper<P, C, T>::dispatch(
167  c, s, std::forward<T>(t), std::forward<Rest>(rest)...))
168 {
169  return detail::dispatch_session_helper<P, C, T>::dispatch(
170  std::forward<C>(c), s, std::forward<T>(t),
171  std::forward<Rest>(rest)...);
172 }
173 
174 
175 //! Trivial session allocator that just calls new and delete.
176 template<typename S> struct session_allocator {
177  constexpr session_allocator() {}
178  S *allocate(rpc_sock *s) { return new S{s}; }
179  void deallocate(S *session) { delete session; }
180 };
181 template<> struct session_allocator<void> {
182  constexpr session_allocator() {}
183  void *allocate(rpc_sock *s) { return nullptr; }
184  void deallocate(void *) {}
185 };
186 
187 
188 struct service_base {
189  using cb_t = std::function<void(msg_ptr)>;
190 
191  const uint32_t prog_;
192  const uint32_t vers_;
193 
194  service_base(uint32_t prog, uint32_t vers) : prog_(prog), vers_(vers) {}
195  virtual ~service_base() {}
196  virtual void process(void *session, rpc_msg &hdr, xdr_get &g, cb_t reply) = 0;
197 
198  bool check_call(const rpc_msg &hdr) {
199  return hdr.body.mtype() == CALL
200  && hdr.body.cbody().rpcvers == 2
201  && hdr.body.cbody().prog == prog_
202  && hdr.body.cbody().vers == vers_;
203  }
204 
205  template<typename T> static bool decode_arg(xdr_get &g, T &arg) {
206  try {
207  archive(g, arg);
208  g.done();
209  return true;
210  }
211  catch (const xdr_runtime_error &) {
212  return false;
213  }
214  }
215 };
216 
218  std::map<uint32_t,
219  std::map<uint32_t, std::unique_ptr<service_base>>> servers_;
220 protected:
221  void register_service_base(service_base *s);
222 public:
223  void dispatch(void *session, msg_ptr m, service_base::cb_t reply);
224 };
225 
226 
227 //! Listens for connections on a TCP socket (optionally registering
228 //! the socket with \c rpcbind), and then serves one or more
229 //! program/version interfaces to accepted connections.
231  void accept_cb();
232  void receive_cb(rpc_sock *ms, void *session, msg_ptr mp);
233 
234 protected:
235  unique_sock listen_sock_;
236  const bool use_rpcbind_;
238  bool use_rpcbind = false);
240  : rpc_tcp_listener_common(ps, unique_sock(invalid_sock), true) {}
241  virtual ~rpc_tcp_listener_common();
242  virtual void *session_alloc(rpc_sock *) = 0;
243  virtual void session_free(void *session) = 0;
244 
245 public:
246  pollset &ps_;
247 };
248 
249 template<template<typename, typename, typename> class ServiceType,
250  typename Session, typename SessionAllocator>
252  SessionAllocator sa_;
253 protected:
254  void *session_alloc(rpc_sock *s) override {
255  return sa_.allocate(s);
256  }
257  void session_free(void *session) override { sa_.deallocate(session); }
258 public:
259  //using rpc_tcp_listener_common::rpc_tcp_listener_common;
261  : rpc_tcp_listener_common(ps) {}
262  generic_rpc_tcp_listener(pollset &ps, unique_sock &&s, bool use_rpcbind,
263  SessionAllocator sa)
264  : rpc_tcp_listener_common(ps, std::move(s), use_rpcbind), sa_(sa) {}
266 
267  //! Add objects implementing RPC program interfaces to the server.
268  template<typename T, typename Interface = typename T::rpc_interface_type>
269  void register_service(T &t) {
270  register_service_base(new ServiceType<T,Session,Interface>(t));
271  if(use_rpcbind_)
272  rpcbind_register(listen_sock_.get(), Interface::program,
273  Interface::version);
274  }
275 };
276 
277 
278 } // namespace xdr
279 
280 #endif // !_XDRPP_SERVER_H_HEADER_INCLUDED_
Structure that gets marshalled as an RPC success header.
Definition: server.h:27
Most of the xdrpp library is encapsulated in the xdr namespace.
Definition: arpc.cc:4
Archive type for unmarshaling from a buffer.
Definition: marshal.h:130
auto dispatch_with_session(C &&c, S *s, T &&t, Rest &&...rest) -> decltype(detail::dispatch_session_helper< P, C, T >::dispatch(c, s, std::forward< T >(t), std::forward< Rest >(rest)...))
Call P::dispatch with a session pointer (unless the session type S is void, in which case the argumen...
Definition: server.h:165
Structure to poll for a set of file descriptors and timeouts.
Definition: pollset.h:23
A wrapper around xdr::msg_sock that separates calls from replies.
Definition: msgsock.h:92
Generic class of XDR unmarshaling errors.
Definition: types.h:41
Default xdr_traits values for actual XDR types, used as a supertype for most xdr::xdr_traits speciali...
Definition: types.h:191
Self-closing socket.
Definition: socket.h:124
Trivial session allocator that just calls new and delete.
Definition: server.h:176
Metadata for all marshalable XDR types.
Definition: types.h:146
Send and receive delimited messages over non-blocking sockets.
void register_service(T &t)
Add objects implementing RPC program interfaces to the server.
Definition: server.h:269
Support for registering and (upon exit) unregistering with rpcbind.
typename detail::wrap_transparent_ptr_helper< T >::type wrap_transparent_ptr
Wrap xdr::transparent_ptr around each type in a tuple to generate a new tuple type.
Definition: server.h:118
A pointer, but that gets marshalled as the underlying object and can convert to the underlying type...
Definition: server.h:63
Support for marshaling XDR types in the format specified by RFC4506.
Listens for connections on a TCP socket (optionally registering the socket with rpcbind), and then serves one or more program/version interfaces to accepted connections.
Definition: server.h:230
void rpcbind_register(const sockaddr *sa, socklen_t salen, std::uint32_t prog, std::uint32_t vers)
Register a service listening on sa with rpcbind.
Definition: rpcbind.cc:130
Support for pretty-printing XDR data types.