CIPCServerSideClient.h 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #pragma once
  2. #include <LibCore/CObject.h>
  3. #include <LibCore/CEvent.h>
  4. #include <LibCore/CEventLoop.h>
  5. #include <LibCore/CIODevice.h>
  6. #include <LibCore/CNotifier.h>
  7. #include <errno.h>
  8. #include <unistd.h>
  9. #include <sys/uio.h>
  10. #include <sys/types.h>
  11. #include <sys/socket.h>
  12. #include <stdio.h>
  13. //#define CIPC_DEBUG
  14. class CIPCServerEvent : public CEvent {
  15. public:
  16. enum Type {
  17. Invalid = 2000,
  18. ClientDisconnected,
  19. };
  20. CIPCServerEvent() {}
  21. explicit CIPCServerEvent(Type type)
  22. : CEvent(type)
  23. {
  24. }
  25. };
  26. class ASClientDisconnectedNotification : public CIPCServerEvent {
  27. public:
  28. explicit ASClientDisconnectedNotification(int client_id)
  29. : CIPCServerEvent(ClientDisconnected)
  30. , m_client_id(client_id)
  31. {
  32. }
  33. int client_id() const { return m_client_id; }
  34. private:
  35. int m_client_id { 0 };
  36. };
  37. template <typename T, class... Args>
  38. T* CIPCServerSideClientCreator(Args&& ... args)
  39. {
  40. auto conn = new T(AK::forward<Args>(args)...) /* arghs */;
  41. conn->send_greeting();
  42. return conn;
  43. };
  44. template <typename ServerMessage, typename ClientMessage>
  45. class CIPCServerSideClient : public CObject
  46. {
  47. public:
  48. CIPCServerSideClient(int fd, int client_id)
  49. : m_socket(fd)
  50. , m_notifier(CNotifier(fd, CNotifier::Read))
  51. , m_client_id(client_id)
  52. {
  53. m_notifier.on_ready_to_read = [this] { drain_client(); };
  54. #if defined(CIPC_DEBUG)
  55. dbg() << "S: Created new CIPCServerSideClient " << fd << client_id << " and said hello";
  56. #endif
  57. }
  58. ~CIPCServerSideClient()
  59. {
  60. #if defined(CIPC_DEBUG)
  61. dbg() << "S: Destroyed CIPCServerSideClient " << m_socket.fd() << client_id();
  62. #endif
  63. }
  64. void post_message(const ServerMessage& message, const ByteBuffer& extra_data = {})
  65. {
  66. #if defined(CIPC_DEBUG)
  67. dbg() << "S: -> C " << int(message.type) << " extra " << extra_data.size();
  68. #endif
  69. if (!extra_data.is_empty())
  70. const_cast<ServerMessage&>(message).extra_size = extra_data.size();
  71. struct iovec iov[2];
  72. int iov_count = 1;
  73. iov[0].iov_base = const_cast<ServerMessage*>(&message);
  74. iov[0].iov_len = sizeof(message);
  75. if (!extra_data.is_empty()) {
  76. iov[1].iov_base = const_cast<u8*>(extra_data.data());
  77. iov[1].iov_len = extra_data.size();
  78. ++iov_count;
  79. }
  80. int nwritten = writev(m_socket.fd(), iov, iov_count);
  81. if (nwritten < 0) {
  82. switch (errno) {
  83. case EPIPE:
  84. dbgprintf("WSClientConnection::post_message: Disconnected from peer.\n");
  85. delete_later();
  86. return;
  87. break;
  88. case EAGAIN:
  89. dbgprintf("WSClientConnection::post_message: Client buffer overflowed.\n");
  90. did_misbehave();
  91. return;
  92. break;
  93. default:
  94. perror("WSClientConnection::post_message writev");
  95. ASSERT_NOT_REACHED();
  96. }
  97. }
  98. ASSERT(nwritten == (int)(sizeof(message) + extra_data.size()));
  99. }
  100. void drain_client()
  101. {
  102. unsigned messages_received = 0;
  103. for (;;) {
  104. ClientMessage message;
  105. // FIXME: Don't go one message at a time, that's so much context switching, oof.
  106. ssize_t nread = recv(m_socket.fd(), &message, sizeof(ClientMessage), MSG_DONTWAIT);
  107. if (nread == 0 || (nread == -1 && errno == EAGAIN)) {
  108. if (!messages_received) {
  109. // TODO: is delete_later() sufficient?
  110. CEventLoop::current().post_event(*this, make<ASClientDisconnectedNotification>(client_id()));
  111. }
  112. break;
  113. }
  114. if (nread < 0) {
  115. perror("recv");
  116. ASSERT_NOT_REACHED();
  117. }
  118. ByteBuffer extra_data;
  119. if (message.extra_size) {
  120. if (message.extra_size >= 32768) {
  121. dbgprintf("message.extra_size is way too large\n");
  122. return did_misbehave();
  123. }
  124. extra_data = ByteBuffer::create_uninitialized(message.extra_size);
  125. // FIXME: We should allow this to time out. Maybe use a socket timeout?
  126. int extra_nread = read(m_socket.fd(), extra_data.data(), extra_data.size());
  127. if (extra_nread != (int)message.extra_size) {
  128. dbgprintf("extra_nread(%d) != extra_size(%d)\n", extra_nread, extra_data.size());
  129. if (extra_nread < 0)
  130. perror("read");
  131. return did_misbehave();
  132. }
  133. }
  134. #if defined(CIPC_DEBUG)
  135. dbg() << "S: <- C " << int(message.type) << " extra " << extra_data.size();
  136. #endif
  137. if (!handle_message(message, move(extra_data)))
  138. return;
  139. ++messages_received;
  140. }
  141. }
  142. void did_misbehave()
  143. {
  144. dbgprintf("CIPCServerSideClient{%p} (id=%d, pid=%d) misbehaved, disconnecting.\n", this, client_id(), m_pid);
  145. delete_later();
  146. m_notifier.set_enabled(false);
  147. }
  148. const char* class_name() const override { return "CIPCServerSideClient"; }
  149. int client_id() const { return m_client_id; }
  150. pid_t client_pid() const { return m_pid; }
  151. void set_client_pid(pid_t pid) { m_pid = pid; }
  152. // ### having this public is sad
  153. virtual void send_greeting() = 0;
  154. protected:
  155. void event(CEvent& event)
  156. {
  157. if (event.type() == CIPCServerEvent::ClientDisconnected) {
  158. int client_id = static_cast<const ASClientDisconnectedNotification&>(event).client_id();
  159. dbgprintf("CIPCServerSideClient: Client disconnected: %d\n", client_id);
  160. delete this;
  161. return;
  162. }
  163. CObject::event(event);
  164. }
  165. virtual bool handle_message(const ClientMessage&, const ByteBuffer&& = {}) = 0;
  166. private:
  167. // TODO: A way to create some kind of CIODevice with an open FD would be nice.
  168. class COpenedSocket : public CIODevice
  169. {
  170. public:
  171. const char* class_name() const override { return "COpenedSocket"; }
  172. COpenedSocket(int fd)
  173. {
  174. set_fd(fd);
  175. set_mode(CIODevice::OpenMode::ReadWrite);
  176. }
  177. bool open(CIODevice::OpenMode) override
  178. {
  179. ASSERT_NOT_REACHED();
  180. return true;
  181. };
  182. int fd() const { return CIODevice::fd(); }
  183. };
  184. COpenedSocket m_socket;
  185. CNotifier m_notifier;
  186. int m_client_id;
  187. int m_pid;
  188. };