cmServer.cxx 15 KB

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