cmServer.cxx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmServer.h"
  4. #include "cmAlgorithms.h"
  5. #include "cmConnection.h"
  6. #include "cmFileMonitor.h"
  7. #include "cmServerDictionary.h"
  8. #include "cmServerProtocol.h"
  9. #include "cmSystemTools.h"
  10. #include "cm_jsoncpp_reader.h"
  11. #include "cm_jsoncpp_writer.h"
  12. #include "cmake.h"
  13. #include "cmsys/FStream.hxx"
  14. #include <algorithm>
  15. #include <cassert>
  16. #include <cstdint>
  17. #include <memory>
  18. #include <utility>
  19. void on_signal(uv_signal_t* signal, int signum)
  20. {
  21. auto conn = reinterpret_cast<cmServerBase*>(signal->data);
  22. conn->OnSignal(signum);
  23. }
  24. static void on_walk_to_shutdown(uv_handle_t* handle, void* arg)
  25. {
  26. (void)arg;
  27. if (!uv_is_closing(handle)) {
  28. uv_close(handle, &cmEventBasedConnection::on_close);
  29. }
  30. }
  31. class cmServer::DebugInfo
  32. {
  33. public:
  34. DebugInfo()
  35. : StartTime(uv_hrtime())
  36. {
  37. }
  38. bool PrintStatistics = false;
  39. std::string OutputFile;
  40. uint64_t StartTime;
  41. };
  42. cmServer::cmServer(cmConnection* conn, bool supportExperimental)
  43. : cmServerBase(conn)
  44. , SupportExperimental(supportExperimental)
  45. {
  46. // Register supported protocols:
  47. this->RegisterProtocol(new cmServerProtocol1);
  48. }
  49. cmServer::~cmServer()
  50. {
  51. if (!this->Protocol) { // Server was never fully started!
  52. return;
  53. }
  54. for (cmServerProtocol* p : this->SupportedProtocols) {
  55. delete p;
  56. }
  57. }
  58. void cmServer::ProcessRequest(cmConnection* connection,
  59. const std::string& input)
  60. {
  61. Json::Reader reader;
  62. Json::Value value;
  63. if (!reader.parse(input, value)) {
  64. this->WriteParseError(connection, "Failed to parse JSON input.");
  65. return;
  66. }
  67. std::unique_ptr<DebugInfo> debug;
  68. Json::Value debugValue = value["debug"];
  69. if (!debugValue.isNull()) {
  70. debug = cm::make_unique<DebugInfo>();
  71. debug->OutputFile = debugValue["dumpToFile"].asString();
  72. debug->PrintStatistics = debugValue["showStats"].asBool();
  73. }
  74. const cmServerRequest request(this, connection, value[kTYPE_KEY].asString(),
  75. value[kCOOKIE_KEY].asString(), value);
  76. if (request.Type == "") {
  77. cmServerResponse response(request);
  78. response.SetError("No type given in request.");
  79. this->WriteResponse(connection, response, nullptr);
  80. return;
  81. }
  82. cmSystemTools::SetMessageCallback(reportMessage,
  83. const_cast<cmServerRequest*>(&request));
  84. if (this->Protocol) {
  85. this->Protocol->CMakeInstance()->SetProgressCallback(
  86. reportProgress, const_cast<cmServerRequest*>(&request));
  87. this->WriteResponse(connection, this->Protocol->Process(request),
  88. debug.get());
  89. } else {
  90. this->WriteResponse(connection, this->SetProtocolVersion(request),
  91. debug.get());
  92. }
  93. }
  94. void cmServer::RegisterProtocol(cmServerProtocol* protocol)
  95. {
  96. if (protocol->IsExperimental() && !this->SupportExperimental) {
  97. return;
  98. }
  99. auto version = protocol->ProtocolVersion();
  100. assert(version.first >= 0);
  101. assert(version.second >= 0);
  102. auto it = std::find_if(this->SupportedProtocols.begin(),
  103. this->SupportedProtocols.end(),
  104. [version](cmServerProtocol* p) {
  105. return p->ProtocolVersion() == version;
  106. });
  107. if (it == this->SupportedProtocols.end()) {
  108. this->SupportedProtocols.push_back(protocol);
  109. }
  110. }
  111. void cmServer::PrintHello(cmConnection* connection) const
  112. {
  113. Json::Value hello = Json::objectValue;
  114. hello[kTYPE_KEY] = "hello";
  115. Json::Value& protocolVersions = hello[kSUPPORTED_PROTOCOL_VERSIONS] =
  116. Json::arrayValue;
  117. for (auto const& proto : this->SupportedProtocols) {
  118. auto version = proto->ProtocolVersion();
  119. Json::Value tmp = Json::objectValue;
  120. tmp[kMAJOR_KEY] = version.first;
  121. tmp[kMINOR_KEY] = version.second;
  122. if (proto->IsExperimental()) {
  123. tmp[kIS_EXPERIMENTAL_KEY] = true;
  124. }
  125. protocolVersions.append(tmp);
  126. }
  127. this->WriteJsonObject(connection, hello, nullptr);
  128. }
  129. void cmServer::reportProgress(const char* msg, float progress, void* data)
  130. {
  131. const cmServerRequest* request = static_cast<const cmServerRequest*>(data);
  132. assert(request);
  133. if (progress < 0.0f || progress > 1.0f) {
  134. request->ReportMessage(msg, "");
  135. } else {
  136. request->ReportProgress(0, static_cast<int>(progress * 1000), 1000, msg);
  137. }
  138. }
  139. void cmServer::reportMessage(const char* msg, const char* title,
  140. bool& /* cancel */, void* data)
  141. {
  142. const cmServerRequest* request = static_cast<const cmServerRequest*>(data);
  143. assert(request);
  144. assert(msg);
  145. std::string titleString;
  146. if (title) {
  147. titleString = title;
  148. }
  149. request->ReportMessage(std::string(msg), titleString);
  150. }
  151. cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request)
  152. {
  153. if (request.Type != kHANDSHAKE_TYPE) {
  154. return request.ReportError("Waiting for type \"" + kHANDSHAKE_TYPE +
  155. "\".");
  156. }
  157. Json::Value requestedProtocolVersion = request.Data[kPROTOCOL_VERSION_KEY];
  158. if (requestedProtocolVersion.isNull()) {
  159. return request.ReportError("\"" + kPROTOCOL_VERSION_KEY +
  160. "\" is required for \"" + kHANDSHAKE_TYPE +
  161. "\".");
  162. }
  163. if (!requestedProtocolVersion.isObject()) {
  164. return request.ReportError("\"" + kPROTOCOL_VERSION_KEY +
  165. "\" must be a JSON object.");
  166. }
  167. Json::Value majorValue = requestedProtocolVersion[kMAJOR_KEY];
  168. if (!majorValue.isInt()) {
  169. return request.ReportError("\"" + kMAJOR_KEY +
  170. "\" must be set and an integer.");
  171. }
  172. Json::Value minorValue = requestedProtocolVersion[kMINOR_KEY];
  173. if (!minorValue.isNull() && !minorValue.isInt()) {
  174. return request.ReportError("\"" + kMINOR_KEY +
  175. "\" must be unset or an integer.");
  176. }
  177. const int major = majorValue.asInt();
  178. const int minor = minorValue.isNull() ? -1 : minorValue.asInt();
  179. if (major < 0) {
  180. return request.ReportError("\"" + kMAJOR_KEY + "\" must be >= 0.");
  181. }
  182. if (!minorValue.isNull() && minor < 0) {
  183. return request.ReportError("\"" + kMINOR_KEY +
  184. "\" must be >= 0 when set.");
  185. }
  186. this->Protocol =
  187. this->FindMatchingProtocol(this->SupportedProtocols, major, minor);
  188. if (!this->Protocol) {
  189. return request.ReportError("Protocol version not supported.");
  190. }
  191. std::string errorMessage;
  192. if (!this->Protocol->Activate(this, request, &errorMessage)) {
  193. this->Protocol = nullptr;
  194. return request.ReportError("Failed to activate protocol version: " +
  195. errorMessage);
  196. }
  197. return request.Reply(Json::objectValue);
  198. }
  199. bool cmServer::Serve(std::string* errorMessage)
  200. {
  201. if (this->SupportedProtocols.empty()) {
  202. *errorMessage =
  203. "No protocol versions defined. Maybe you need --experimental?";
  204. return false;
  205. }
  206. assert(!this->Protocol);
  207. return cmServerBase::Serve(errorMessage);
  208. }
  209. cmFileMonitor* cmServer::FileMonitor() const
  210. {
  211. return fileMonitor.get();
  212. }
  213. void cmServer::WriteJsonObject(const Json::Value& jsonValue,
  214. const DebugInfo* debug) const
  215. {
  216. for (auto& connection : this->Connections) {
  217. WriteJsonObject(connection.get(), jsonValue, debug);
  218. }
  219. }
  220. void cmServer::WriteJsonObject(cmConnection* connection,
  221. const Json::Value& jsonValue,
  222. const DebugInfo* debug) const
  223. {
  224. Json::FastWriter writer;
  225. auto beforeJson = uv_hrtime();
  226. std::string result = writer.write(jsonValue);
  227. if (debug) {
  228. Json::Value copy = jsonValue;
  229. if (debug->PrintStatistics) {
  230. Json::Value stats = Json::objectValue;
  231. auto endTime = uv_hrtime();
  232. stats["jsonSerialization"] = double(endTime - beforeJson) / 1000000.0;
  233. stats["totalTime"] = double(endTime - debug->StartTime) / 1000000.0;
  234. stats["size"] = static_cast<int>(result.size());
  235. if (!debug->OutputFile.empty()) {
  236. stats["dumpFile"] = debug->OutputFile;
  237. }
  238. copy["zzzDebug"] = stats;
  239. result = writer.write(copy); // Update result to include debug info
  240. }
  241. if (!debug->OutputFile.empty()) {
  242. cmsys::ofstream myfile(debug->OutputFile.c_str());
  243. myfile << result;
  244. }
  245. }
  246. connection->WriteData(std::string("\n") + kSTART_MAGIC + std::string("\n") +
  247. result + kEND_MAGIC + std::string("\n"));
  248. }
  249. cmServerProtocol* cmServer::FindMatchingProtocol(
  250. const std::vector<cmServerProtocol*>& protocols, int major, int minor)
  251. {
  252. cmServerProtocol* bestMatch = nullptr;
  253. for (auto protocol : protocols) {
  254. auto version = protocol->ProtocolVersion();
  255. if (major != version.first) {
  256. continue;
  257. }
  258. if (minor == version.second) {
  259. return protocol;
  260. }
  261. if (!bestMatch || bestMatch->ProtocolVersion().second < version.second) {
  262. bestMatch = protocol;
  263. }
  264. }
  265. return minor < 0 ? bestMatch : nullptr;
  266. }
  267. void cmServer::WriteProgress(const cmServerRequest& request, int min,
  268. int current, int max,
  269. const std::string& message) const
  270. {
  271. assert(min <= current && current <= max);
  272. assert(message.length() != 0);
  273. Json::Value obj = Json::objectValue;
  274. obj[kTYPE_KEY] = kPROGRESS_TYPE;
  275. obj[kREPLY_TO_KEY] = request.Type;
  276. obj[kCOOKIE_KEY] = request.Cookie;
  277. obj[kPROGRESS_MESSAGE_KEY] = message;
  278. obj[kPROGRESS_MINIMUM_KEY] = min;
  279. obj[kPROGRESS_MAXIMUM_KEY] = max;
  280. obj[kPROGRESS_CURRENT_KEY] = current;
  281. this->WriteJsonObject(request.Connection, obj, nullptr);
  282. }
  283. void cmServer::WriteMessage(const cmServerRequest& request,
  284. const std::string& message,
  285. const std::string& title) const
  286. {
  287. if (message.empty()) {
  288. return;
  289. }
  290. Json::Value obj = Json::objectValue;
  291. obj[kTYPE_KEY] = kMESSAGE_TYPE;
  292. obj[kREPLY_TO_KEY] = request.Type;
  293. obj[kCOOKIE_KEY] = request.Cookie;
  294. obj[kMESSAGE_KEY] = message;
  295. if (!title.empty()) {
  296. obj[kTITLE_KEY] = title;
  297. }
  298. WriteJsonObject(request.Connection, obj, nullptr);
  299. }
  300. void cmServer::WriteParseError(cmConnection* connection,
  301. const std::string& message) const
  302. {
  303. Json::Value obj = Json::objectValue;
  304. obj[kTYPE_KEY] = kERROR_TYPE;
  305. obj[kERROR_MESSAGE_KEY] = message;
  306. obj[kREPLY_TO_KEY] = "";
  307. obj[kCOOKIE_KEY] = "";
  308. this->WriteJsonObject(connection, obj, nullptr);
  309. }
  310. void cmServer::WriteSignal(const std::string& name,
  311. const Json::Value& data) const
  312. {
  313. assert(data.isObject());
  314. Json::Value obj = data;
  315. obj[kTYPE_KEY] = kSIGNAL_TYPE;
  316. obj[kREPLY_TO_KEY] = "";
  317. obj[kCOOKIE_KEY] = "";
  318. obj[kNAME_KEY] = name;
  319. WriteJsonObject(obj, nullptr);
  320. }
  321. void cmServer::WriteResponse(cmConnection* connection,
  322. const cmServerResponse& response,
  323. const DebugInfo* debug) const
  324. {
  325. assert(response.IsComplete());
  326. Json::Value obj = response.Data();
  327. obj[kCOOKIE_KEY] = response.Cookie;
  328. obj[kTYPE_KEY] = response.IsError() ? kERROR_TYPE : kREPLY_TYPE;
  329. obj[kREPLY_TO_KEY] = response.Type;
  330. if (response.IsError()) {
  331. obj[kERROR_MESSAGE_KEY] = response.ErrorMessage();
  332. }
  333. this->WriteJsonObject(connection, obj, debug);
  334. }
  335. void cmServer::OnConnected(cmConnection* connection)
  336. {
  337. PrintHello(connection);
  338. }
  339. void cmServer::OnServeStart()
  340. {
  341. cmServerBase::OnServeStart();
  342. fileMonitor = std::make_shared<cmFileMonitor>(GetLoop());
  343. }
  344. void cmServer::StartShutDown()
  345. {
  346. if (fileMonitor) {
  347. fileMonitor->StopMonitoring();
  348. fileMonitor.reset();
  349. }
  350. cmServerBase::StartShutDown();
  351. }
  352. static void __start_thread(void* arg)
  353. {
  354. auto server = reinterpret_cast<cmServerBase*>(arg);
  355. std::string error;
  356. server->Serve(&error);
  357. }
  358. bool cmServerBase::StartServeThread()
  359. {
  360. ServeThreadRunning = true;
  361. uv_thread_create(&ServeThread, __start_thread, this);
  362. return true;
  363. }
  364. bool cmServerBase::Serve(std::string* errorMessage)
  365. {
  366. errorMessage->clear();
  367. uv_signal_init(&Loop, &this->SIGINTHandler);
  368. uv_signal_init(&Loop, &this->SIGHUPHandler);
  369. this->SIGINTHandler.data = this;
  370. this->SIGHUPHandler.data = this;
  371. uv_signal_start(&this->SIGINTHandler, &on_signal, SIGINT);
  372. uv_signal_start(&this->SIGHUPHandler, &on_signal, SIGHUP);
  373. OnServeStart();
  374. for (auto& connection : Connections) {
  375. if (!connection->OnServeStart(errorMessage)) {
  376. return false;
  377. }
  378. }
  379. if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
  380. *errorMessage = "Internal Error: Event loop stopped in unclean state.";
  381. StartShutDown();
  382. return false;
  383. }
  384. ServeThreadRunning = false;
  385. return true;
  386. }
  387. void cmServerBase::OnConnected(cmConnection*)
  388. {
  389. }
  390. void cmServerBase::OnDisconnect()
  391. {
  392. }
  393. void cmServerBase::OnServeStart()
  394. {
  395. uv_signal_start(&this->SIGINTHandler, &on_signal, SIGINT);
  396. uv_signal_start(&this->SIGHUPHandler, &on_signal, SIGHUP);
  397. }
  398. void cmServerBase::StartShutDown()
  399. {
  400. if (!uv_is_closing(
  401. reinterpret_cast<const uv_handle_t*>(&this->SIGINTHandler))) {
  402. uv_signal_stop(&this->SIGINTHandler);
  403. }
  404. if (!uv_is_closing(
  405. reinterpret_cast<const uv_handle_t*>(&this->SIGHUPHandler))) {
  406. uv_signal_stop(&this->SIGHUPHandler);
  407. }
  408. for (auto& connection : Connections) {
  409. connection->OnConnectionShuttingDown();
  410. }
  411. Connections.clear();
  412. uv_stop(&Loop);
  413. uv_walk(&Loop, on_walk_to_shutdown, nullptr);
  414. uv_run(&Loop, UV_RUN_DEFAULT);
  415. }
  416. bool cmServerBase::OnSignal(int signum)
  417. {
  418. (void)signum;
  419. StartShutDown();
  420. return true;
  421. }
  422. cmServerBase::cmServerBase(cmConnection* connection)
  423. {
  424. uv_loop_init(&Loop);
  425. uv_signal_init(&Loop, &this->SIGINTHandler);
  426. uv_signal_init(&Loop, &this->SIGHUPHandler);
  427. this->SIGINTHandler.data = this;
  428. this->SIGHUPHandler.data = this;
  429. AddNewConnection(connection);
  430. }
  431. cmServerBase::~cmServerBase()
  432. {
  433. if (ServeThreadRunning) {
  434. StartShutDown();
  435. uv_thread_join(&ServeThread);
  436. }
  437. uv_loop_close(&Loop);
  438. }
  439. void cmServerBase::AddNewConnection(cmConnection* ownedConnection)
  440. {
  441. Connections.emplace_back(ownedConnection);
  442. ownedConnection->SetServer(this);
  443. }
  444. uv_loop_t* cmServerBase::GetLoop()
  445. {
  446. return &Loop;
  447. }
  448. void cmServerBase::OnDisconnect(cmConnection* pConnection)
  449. {
  450. auto pred = [pConnection](const std::unique_ptr<cmConnection>& m) {
  451. return m.get() == pConnection;
  452. };
  453. Connections.erase(
  454. std::remove_if(Connections.begin(), Connections.end(), pred),
  455. Connections.end());
  456. if (Connections.empty()) {
  457. StartShutDown();
  458. }
  459. }