console_server.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. /*
  2. * Copyright (c) 2012-2020 Daniele Bartolini and individual contributors.
  3. * License: https://github.com/dbartolini/crown/blob/master/LICENSE
  4. */
  5. #include "core/containers/array.inl"
  6. #include "core/containers/hash_map.inl"
  7. #include "core/containers/vector.inl"
  8. #include "core/json/json_object.inl"
  9. #include "core/json/sjson.h"
  10. #include "core/memory/temp_allocator.inl"
  11. #include "core/strings/dynamic_string.inl"
  12. #include "core/strings/string_id.inl"
  13. #include "core/strings/string_stream.inl"
  14. #include "device/console_server.h"
  15. LOG_SYSTEM(CONSOLE_SERVER, "console_server")
  16. namespace crown
  17. {
  18. namespace console_server_internal
  19. {
  20. static void message_command(ConsoleServer& cs, TCPSocket& client, const char* json, void* /*user_data*/)
  21. {
  22. TempAllocator4096 ta;
  23. JsonObject obj(ta);
  24. JsonArray args(ta);
  25. sjson::parse(obj, json);
  26. sjson::parse_array(args, obj["args"]);
  27. DynamicString command_name(ta);
  28. sjson::parse_string(command_name, args[0]);
  29. ConsoleServer::CommandData cmd;
  30. cmd.command_function = NULL;
  31. cmd.user_data = NULL;
  32. cmd = hash_map::get(cs._commands, command_name.to_string_id(), cmd);
  33. if (cmd.command_function != NULL)
  34. cmd.command_function(cs, client, args, cmd.user_data);
  35. }
  36. static void command_help(ConsoleServer& cs, TCPSocket& client, JsonArray& args, void* /*user_data*/)
  37. {
  38. if (array::size(args) != 1)
  39. {
  40. cs.error(client, "Usage: help");
  41. return;
  42. }
  43. u32 longest = 0;
  44. auto cur = hash_map::begin(cs._commands);
  45. auto end = hash_map::end(cs._commands);
  46. for (; cur != end; ++cur)
  47. {
  48. HASH_MAP_SKIP_HOLE(cs._commands, cur);
  49. if (longest < strlen32(cur->second.name))
  50. longest = strlen32(cur->second.name);
  51. }
  52. cur = hash_map::begin(cs._commands);
  53. end = hash_map::end(cs._commands);
  54. for (; cur != end; ++cur)
  55. {
  56. HASH_MAP_SKIP_HOLE(cs._commands, cur);
  57. logi(CONSOLE_SERVER, "%s%*s%s"
  58. , cur->second.name
  59. , longest - strlen32(cur->second.name) + 2
  60. , " "
  61. , cur->second.brief
  62. );
  63. }
  64. }
  65. static u32 add_client(ConsoleServer& cs, const TCPSocket& socket)
  66. {
  67. const u32 id = cs._next_client_id++;
  68. ConsoleServer::Client client;
  69. client.socket = socket;
  70. client.id = id;
  71. vector::push_back(cs._clients, client);
  72. return id;
  73. }
  74. static void remove_client(ConsoleServer& cs, u32 id)
  75. {
  76. const u32 last = vector::size(cs._clients) - 1;
  77. for (u32 cc = 0; cc < vector::size(cs._clients); ++cc)
  78. {
  79. if (cs._clients[cc].id == id)
  80. {
  81. cs._clients[cc].socket.close();
  82. cs._clients[cc] = cs._clients[last];
  83. vector::pop_back(cs._clients);
  84. return;
  85. }
  86. }
  87. }
  88. } // namespace console_server_internal
  89. ConsoleServer::ConsoleServer(Allocator& a)
  90. : _next_client_id(0)
  91. , _clients(a)
  92. , _messages(a)
  93. , _commands(a)
  94. {
  95. this->register_message_type("command", console_server_internal::message_command, this);
  96. this->register_command_name("help", "List all commands", console_server_internal::command_help, this);
  97. }
  98. void ConsoleServer::listen(u16 port, bool wait)
  99. {
  100. _server.bind(port);
  101. _server.listen(5);
  102. if (wait)
  103. {
  104. AcceptResult ar;
  105. TCPSocket client;
  106. do
  107. {
  108. ar = _server.accept(client);
  109. }
  110. while (ar.error != AcceptResult::SUCCESS);
  111. console_server_internal::add_client(*this, client);
  112. }
  113. }
  114. void ConsoleServer::shutdown()
  115. {
  116. for (u32 i = 0; i < vector::size(_clients); ++i)
  117. _clients[i].socket.close();
  118. _server.close();
  119. }
  120. void ConsoleServer::send(TCPSocket& client, const char* json)
  121. {
  122. u32 len = strlen32(json);
  123. client.write(&len, 4);
  124. client.write(json, len);
  125. }
  126. void ConsoleServer::error(TCPSocket& client, const char* msg)
  127. {
  128. TempAllocator4096 ta;
  129. StringStream ss(ta);
  130. ss << "{\"type\":\"error\",\"message\":\"" << msg << "\"}";
  131. send(client, string_stream::c_str(ss));
  132. }
  133. void ConsoleServer::log(LogSeverity::Enum sev, const char* system, const char* msg)
  134. {
  135. const char* severity_map[] = { "info", "warning", "error" };
  136. CE_STATIC_ASSERT(countof(severity_map) == LogSeverity::COUNT);
  137. if (vector::size(_clients) == 0)
  138. return;
  139. TempAllocator4096 ta;
  140. StringStream ss(ta);
  141. ss << "{\"type\":\"message\",\"severity\":\"";
  142. ss << severity_map[sev];
  143. ss << "\",\"system\":\"";
  144. ss << system;
  145. ss << "\",\"message\":\"";
  146. // Sanitize msg
  147. const char* ch = msg;
  148. for (; *ch; ch++)
  149. {
  150. if (*ch == '"' || *ch == '\\')
  151. ss << "\\";
  152. ss << *ch;
  153. }
  154. ss << "\"}";
  155. send(string_stream::c_str(ss));
  156. }
  157. void ConsoleServer::send(const char* json)
  158. {
  159. for (u32 i = 0; i < vector::size(_clients); ++i)
  160. send(_clients[i].socket, json);
  161. }
  162. void ConsoleServer::update()
  163. {
  164. TCPSocket client;
  165. AcceptResult ar = _server.accept_nonblock(client);
  166. if (ar.error == AcceptResult::SUCCESS)
  167. console_server_internal::add_client(*this, client);
  168. TempAllocator256 alloc;
  169. Array<u32> to_remove(alloc);
  170. // Update all clients
  171. for (u32 i = 0; i < vector::size(_clients); ++i)
  172. {
  173. for (;;)
  174. {
  175. u32 msg_len = 0;
  176. ReadResult rr = _clients[i].socket.read_nonblock(&msg_len, 4);
  177. if (rr.error == ReadResult::WOULDBLOCK)
  178. break;
  179. if (rr.error != ReadResult::SUCCESS)
  180. {
  181. array::push_back(to_remove, _clients[i].id);
  182. break;
  183. }
  184. // Read message
  185. TempAllocator4096 ta;
  186. Array<char> msg(ta);
  187. array::resize(msg, msg_len + 1);
  188. rr = _clients[i].socket.read(array::begin(msg), msg_len);
  189. msg[msg_len] = '\0';
  190. if (rr.error != ReadResult::SUCCESS)
  191. {
  192. array::push_back(to_remove, _clients[i].id);
  193. break;
  194. }
  195. // Process message
  196. JsonObject obj(ta);
  197. sjson::parse(obj, array::begin(msg));
  198. CommandData cmd;
  199. cmd.message_function = NULL;
  200. cmd.user_data = NULL;
  201. cmd = hash_map::get(_messages
  202. , sjson::parse_string_id(obj["type"])
  203. , cmd
  204. );
  205. if (cmd.message_function)
  206. cmd.message_function(*this, _clients[i].socket, array::begin(msg), cmd.user_data);
  207. else
  208. error(_clients[i].socket, "Unknown command");
  209. }
  210. }
  211. // Remove clients
  212. for (u32 ii = 0; ii < array::size(to_remove); ++ii)
  213. console_server_internal::remove_client(*this, to_remove[ii]);
  214. }
  215. void ConsoleServer::register_command_name(const char* name, const char* brief, CommandTypeFunction function, void* user_data)
  216. {
  217. CE_ENSURE(NULL != name);
  218. CE_ENSURE(NULL != brief);
  219. CE_ENSURE(NULL != function);
  220. CommandData cmd;
  221. cmd.command_function = function;
  222. cmd.user_data = user_data;
  223. strncpy(cmd.name, name, sizeof(cmd.name)-1);
  224. strncpy(cmd.brief, brief, sizeof(cmd.brief)-1);
  225. hash_map::set(_commands, StringId32(name), cmd);
  226. }
  227. void ConsoleServer::register_message_type(const char* type, MessageTypeFunction function, void* user_data)
  228. {
  229. CE_ENSURE(NULL != type);
  230. CE_ENSURE(NULL != function);
  231. CommandData cmd;
  232. cmd.message_function = function;
  233. cmd.user_data = user_data;
  234. hash_map::set(_messages, StringId32(type), cmd);
  235. }
  236. namespace console_server_globals
  237. {
  238. ConsoleServer* _console_server = NULL;
  239. void init()
  240. {
  241. _console_server = CE_NEW(default_allocator(), ConsoleServer)(default_allocator());
  242. }
  243. void shutdown()
  244. {
  245. _console_server->shutdown();
  246. CE_DELETE(default_allocator(), _console_server);
  247. _console_server = NULL;
  248. }
  249. } // namespace console_server_globals
  250. ConsoleServer* console_server()
  251. {
  252. return console_server_globals::_console_server;
  253. }
  254. } // namespace crown