libtransistor
A userland library for the Nintendo Switch
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
ipcserver.hpp
Go to the documentation of this file.
1 
6 #pragma once
7 
8 #include<libtransistor/cpp/types.hpp>
11 
13 #include<libtransistor/ipc.h>
14 
15 #include<expected.hpp>
16 
17 #include<vector>
18 #include<forward_list>
19 #include<tuple>
20 
21 namespace trn {
22 namespace ipc {
23 namespace server {
24 
25 class IPCServer;
26 
27 class Object {
28  public:
29  Object(IPCServer *server);
30  virtual ~Object();
31  virtual ResultCode Dispatch(trn::ipc::Message msg, uint32_t request_id) = 0;
32 
33  IPCServer *server;
34  ipc_server_object_t object;
35  protected:
36  private:
37 };
38 
39 class IPCServer {
40  public:
41  static Result<IPCServer> Create(Waiter *waiter, uint32_t max_ports=63, uint32_t max_sessions=63, size_t pointer_buffer_size=0x500);
42 
43  IPCServer() = delete;
44  IPCServer(const IPCServer &) = delete;
45  IPCServer &operator=(const IPCServer &) = delete;
46  IPCServer(IPCServer &&other);
47  IPCServer &operator=(IPCServer &&other);
48  ~IPCServer();
49 
50  template<typename T, class... Args>
51  Result<T*> CreateObject(Object *existing, Args &&... args) {
52  T *obj = new T(this, args...);
53  return ResultCode::ExpectOk(ipc_server_object_register(&existing->object, &obj->object)).map([obj](auto const &ignored) {
54  return obj;
55  });
56  }
57 
58  template<typename T, class... Args>
59  Result<T*> AttachSession(KObject &&object, Args &&... args) {
60  T *obj = new T(this, args...);
61  return ResultCode::ExpectOk(ipc_server_create_session(server, object.Claim(), 0, &obj->object)).map([obj](auto const &ignored) {
62  return obj;
63  });
64  }
65 
66  template<typename T>
67  Result<std::nullopt_t> CreateService(const char *name) {
68  return CreateService(name, [](IPCServer *server) {
69  return new T(server);
70  });
71  }
72  Result<std::nullopt_t> CreateService(const char *name, std::function<Result<Object*>(IPCServer *server)> factory);
73 
74  private:
75  IPCServer(ipc_server_t *server);
76  std::forward_list<std::function<Result<Object*>()>*> factories;
77  ipc_server_t *server;
78 };
79 
80 template<typename... T>
81 struct ArgPack;
82 
86  std::vector<ipc_buffer_t*> buffers;
87  Object **out_objects;
88  uint64_t pid;
89 
90  void Prepare();
92 };
93 
94 template<typename T>
96 
97 template<typename T>
98 struct AccessorHelper<ipc::InRaw<T>> {
99  size_t offset;
100 
101  AccessorHelper(size_t offset) : offset(offset) {
102  }
103 
104  ipc::InRaw<T> Access(TransactionFormat &f) const {
105  return ipc::InRaw<T>(*((T*) (((uint8_t*) f.rq.raw_data) + offset)));
106  }
107 };
108 
109 template<typename T>
110 struct AccessorHelper<ipc::OutRaw<T>> {
111  size_t offset;
112 
113  AccessorHelper(size_t offset) : offset(offset) {
114  }
115 
116  ipc::OutRaw<T> Access(TransactionFormat &f) const {
117  return ipc::OutRaw<T>(*(T*) (((uint8_t*) f.rs.raw_data) + offset));
118  }
119 };
120 
121 template<typename T>
122 struct AccessorHelper<ipc::InHandle<T, ipc::copy>> {
123  size_t index;
124 
125  AccessorHelper(size_t index) : index(index) {
126  }
127 
129  return ipc::InHandle<T, ipc::copy>(f.rq.copy_handles[index]);
130  }
131 };
132 
133 template<typename T>
134 struct AccessorHelper<ipc::InHandle<T, ipc::move>> {
135  size_t index;
136 
137  AccessorHelper(size_t index) : index(index) {
138  }
139 
141  return ipc::InHandle<T, ipc::move>(f.rq.move_handles[index]);
142  }
143 };
144 
145 template<typename T>
146 struct AccessorHelper<ipc::OutHandle<T, ipc::copy>> {
147  size_t index;
148 
149  AccessorHelper(size_t index) : index(index) {
150  }
151 
153  return ipc::OutHandle<T, ipc::copy>(f.rs.copy_handles[index]);
154  }
155 };
156 
157 template<typename T>
158 struct AccessorHelper<ipc::OutHandle<T, ipc::move>> {
159  size_t index;
160 
161  AccessorHelper(size_t index) : index(index) {
162  }
163 
165  return ipc::OutHandle<T, ipc::move>(f.rs.move_handles[index]);
166  }
167 };
168 
169 template<typename T>
170 struct AccessorHelper<ipc::OutObject<T>&> {
171  size_t index;
172 
173  AccessorHelper(size_t index) : index(index) {
174  }
175 
176  ipc::OutObject<T> &Access(TransactionFormat &f) const {
177  return *((ipc::OutObject<T>*) &f.out_objects[index]);
178  }
179 };
180 
181 template<typename T, uint32_t type, size_t expected_size>
182 struct AccessorHelper<ipc::Buffer<T, type, expected_size>> {
183  ipc_buffer_t *buffer;
184 
185  AccessorHelper(ipc_buffer_t *buffer) : buffer(buffer) {
186  }
187 
189  return {(T*) buffer->addr, buffer->size};
190  }
191 };
192 
193 template<>
194 struct AccessorHelper<ipc::InPid> {
195  AccessorHelper() {
196  }
197 
198  ipc::InPid Access(TransactionFormat &f) const {
199  return {f.pid};
200  }
201 };
202 
203 template<>
204 struct AccessorHelper<ipc::OutPid> {
205  AccessorHelper() {
206  }
207 
208  ipc::OutPid Access(TransactionFormat &f) const {
209  return OutPid(f.pid);
210  }
211 };
212 
213 template<typename T>
215 
216 template<typename T>
217 struct FormatMutator<ipc::OutObject<T>&> {
218  static AccessorHelper<ipc::OutObject<T>&> MutateFormat(TransactionFormat &fmt) {
219  return AccessorHelper<ipc::OutObject<T>&>(fmt.rs.num_objects++);
220  }
221 };
222 
223 template<typename T>
224 struct FormatMutator<ipc::InRaw<T>> {
225  static AccessorHelper<ipc::InRaw<T>> MutateFormat(TransactionFormat &fmt) {
226  fmt.rq.raw_data_size+= (alignof(T) - 1);
227  fmt.rq.raw_data_size-= fmt.rq.raw_data_size % alignof(T); // align
228  size_t offset = fmt.rq.raw_data_size;
229  fmt.rq.raw_data_size+= sizeof(T);
230  return AccessorHelper<ipc::InRaw<T>>(offset);
231  }
232 };
233 
234 template<typename T>
235 struct FormatMutator<ipc::OutRaw<T>> {
236  static AccessorHelper<ipc::OutRaw<T>> MutateFormat(TransactionFormat &fmt) {
237  fmt.rs.raw_data_size+= (alignof(T) - 1);
238  fmt.rs.raw_data_size-= fmt.rs.raw_data_size % alignof(T); // align
239  size_t offset = fmt.rs.raw_data_size;
240  fmt.rs.raw_data_size+= sizeof(T);
241  return AccessorHelper<ipc::OutRaw<T>>(offset);
242  }
243 };
244 
245 template<typename T>
246 struct FormatMutator<ipc::InHandle<T, ipc::copy>> {
249  }
250 };
251 
252 template<typename T>
253 struct FormatMutator<ipc::InHandle<T, ipc::move>> {
256  }
257 };
258 
259 template<typename T>
260 struct FormatMutator<ipc::OutHandle<T, ipc::copy>> {
262  return AccessorHelper<ipc::OutHandle<T, ipc::copy>>(fmt.rs.num_copy_handles++);
263  }
264 };
265 
266 template<typename T>
267 struct FormatMutator<ipc::OutHandle<T, ipc::move>> {
269  return AccessorHelper<ipc::OutHandle<T, ipc::move>>(fmt.rs.num_move_handles++);
270  }
271 };
272 
273 template<typename T, uint32_t type, size_t expected_size>
274 struct FormatMutator<ipc::Buffer<T, type, expected_size>> {
276  ipc_buffer_t *buffer = new ipc_buffer_t();
277  buffer->type = type;
278  buffer->size = expected_size;
279  fmt.buffers.push_back(buffer);
281  }
282 };
283 
284 template<>
285 struct FormatMutator<ipc::InPid> {
286  static AccessorHelper<ipc::InPid> MutateFormat(TransactionFormat &fmt) {
287  fmt.rq.send_pid = true;
289  }
290 };
291 
292 template<>
293 struct FormatMutator<ipc::OutPid> {
294  static AccessorHelper<ipc::OutPid> MutateFormat(TransactionFormat &fmt) {
295  fmt.rs.send_pid = true;
297  }
298 };
299 
300 template<typename T>
302 
303 template<typename Arg0, typename... Args>
304 struct FormatBuilder<ArgPack<Arg0, Args...>> {
305  static std::tuple<AccessorHelper<Arg0>, AccessorHelper<Args>...> Build(TransactionFormat &fmt) {
307  return std::tuple_cat(std::make_tuple(helper), FormatBuilder<ArgPack<Args...>>::Build(fmt));
308  }
309 };
310 
311 template<>
313  static std::tuple<> Build(TransactionFormat &fmt) {
314  // nothing to do
315  return std::make_tuple();
316  }
317 };
318 
319 
320 template<auto>
322 
323 template<typename T, typename... Args, ResultCode (T::*Func)(Args...)>
324 struct RequestHandler<Func> {
325  static ResultCode Handle(T *object, ipc::Message msg) {
326  TransactionFormat fmt;
327  std::tuple<AccessorHelper<Args>...> accessors = FormatBuilder<ArgPack<Args...>>::Build(fmt);
328  fmt.Prepare();
329 
330  ResultCode r = ipc_unflatten_request(&msg.msg, &fmt.rq, &object->object);
331  if(!r.IsOk()) {
332  // this gets forwarded to dispatch_shim, which will close the session for us
333  return r;
334  }
335 
336  r = RequestHandler<Func>::Helper(object, fmt, accessors, std::index_sequence_for<Args...>());
337  if(!r.IsOk()) {
339  rs.result_code = r.code;
340  return ipc_server_object_reply(&object->object, &rs);
341  }
342 
343  for(size_t i = 0; i < fmt.rs.num_objects; i++) {
344  fmt.rs.objects[i] = &fmt.out_objects[i]->object;
345  }
346 
347  return ipc_server_object_reply(&object->object, &fmt.rs);
348  }
349  private:
350  template<std::size_t... I>
351  static ResultCode Helper(T *object, TransactionFormat &fmt, const std::tuple<AccessorHelper<Args>...> &accessors, std::index_sequence<I...>) {
352  return std::invoke(Func, object, (std::get<I>(accessors).Access(fmt))...);
353  }
354 };
355 
356 template<typename T, typename... Args, ResultCode (T::*Func)(std::function<void(ResultCode)>, Args...)>
357 struct RequestHandler<Func> {
358  static ResultCode Handle(T *object, ipc::Message msg) {
359  std::shared_ptr<TransactionFormat> fmt = std::make_shared<TransactionFormat>();
360  std::tuple<AccessorHelper<Args>...> accessors = FormatBuilder<ArgPack<Args...>>::Build(*fmt);
361  fmt->Prepare();
362 
363  ResultCode r = ipc_unflatten_request(&msg.msg, &fmt->rq, &object->object);
364  if(!r.IsOk()) {
365  // this gets forwarded to dispatch_shim, which will close the session for us
366  return r;
367  }
368 
369  RequestHandler<Func>::Helper(object, fmt, accessors, std::index_sequence_for<Args...>());
370  }
371  private:
372  template<std::size_t... I>
373  static ResultCode Helper(T *object, std::shared_ptr<TransactionFormat> fmt, const std::tuple<AccessorHelper<Args>...> &accessors, std::index_sequence<I...>) {
374  return std::invoke(
375  Func, object,
376  [object, fmt](ResultCode r) -> void {
377  if(!r.IsOk()) {
379  rs.result_code = r.code;
380  ipc_server_object_reply(&object->object, &rs);
381  return;
382  }
383 
384  for(size_t i = 0; i < fmt->rs.num_objects; i++) {
385  fmt->rs.objects[i] = &fmt->out_objects[i]->object;
386  }
387 
388  if(ipc_server_object_reply(&object->object, &fmt->rs) != RESULT_OK) {
389  ipc_server_session_close(object->object.owning_session);
390  }
391  }, (std::get<I>(accessors).Access(*fmt))...);
392  }
393 };
394 
395 }
396 }
397 }
Represents the server side of an IPC object.
Definition: ipcserver.h:30
Definition: ipcserver.hpp:27
IPC (C++ header)
handle_t * copy_handles
Array to be populated with the expected amount of incoming copy handles.
Definition: ipc.h:110
bool send_pid
Whether to expect an incoming PID.
Definition: ipc.h:105
Definition: waiter.hpp:53
ipc_response_t ipc_default_response
An IPC response with default values set.
Definition: types.hpp:56
Definition: ipcserver.hpp:301
Definition: ipc.hpp:45
Definition: ipc.hpp:139
Definition: ipc.hpp:28
Represents an unmarshalled outgoing IPC response.
Definition: ipc.h:121
IPC Server data structures and functions.
Definition: ipc.hpp:16
Manager for waiting on synchronizable handles (C++ bindings)
size_t raw_data_size
Size in bytes of raw_data to expect.
Definition: ipc.h:104
Definition: ipcserver.hpp:95
ipc_request_fmt_t ipc_default_request_fmt
An IPC request format with default values set.
Describes format expectations for an incoming IPC request.
Definition: ipc.h:100
uint8_t num_move_handles
How many handles to expect to be moved.
Definition: ipc.h:108
handle_t * move_handles
Array to be populated with the expected amount of incoming move handles.
Definition: ipc.h:111
Definition: types.hpp:13
Definition: ipc.hpp:127
Definition: ipc.hpp:65
uint8_t num_copy_handles
How many handles to expect to be copied.
Definition: ipc.h:107
Buffer for transfer over IPC.
Definition: ipc.h:62
result_t ipc_unflatten_request(ipc_message_t *msg, ipc_request_fmt_t *rs, struct ipc_server_object_t *object)
Unflattens the IPC request described by rq from msg
Definition: ipcserver.hpp:321
void * raw_data
Buffer to copy raw request data into.
Definition: ipc.h:103
Definition: ipcserver.hpp:81
Definition: ipc.hpp:34
Interprocess Communication data structures and functions.
uint64_t size
Size in bytes.
Definition: ipc.h:64
Definition: ipcserver.hpp:39
Definition: ipcserver.h:107
Definition: ipc.hpp:89
Definition: ipcserver.hpp:214
Definition: ipc.hpp:135
Definition: ipcserver.hpp:83